home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 20 / Cream of the Crop 20 (Terry Blount) (1996).iso / program / cguide3.zip / CGUIDE_3.TXT < prev    next >
Text File  |  1996-03-18  |  441KB  |  14,500 lines

  1.  
  2.                                     
  3.                    THE IBM PC PROGRAMMER'S GUIDE TO C
  4.                                     
  5.                                     
  6.                                     
  7.                                3rd Edition
  8.                                     
  9.                                     
  10.                                     
  11.                              Matthew Probert
  12.  
  13.  
  14.                                     
  15.                             COPYRIGHT NOTICE
  16.  
  17.  
  18. This publication remains the property of Matthew Probert. License is
  19. hereby given for this work to be freely distibuted in whole under the
  20. proviso that credit is given to the author. Sections of this work may be
  21. used and distributed without payment under the proviso that credit is
  22. given to both this work and the author. Source code occuring in this work
  23. may be used within commercial and non-commercial applications without
  24. charge and without reference to the author.
  25.  
  26.  BIOGRAPHICAL NOTES
  27.  
  28.  
  29. Matthew Probert is a software consultant working for his own firm,
  30. Servile Software. He has been involved with micro-computer software
  31. design and programming since the age of eighteen and has been involved
  32. with the C programming language for the past ten years.
  33.  
  34. His educational background lies in the non-too distinguished honour of
  35. having a nervous break down during his last year of secondary school
  36. which resulted in a lack of formal qualifications. However, Matthew has
  37. made up for it by achieving a complete recovery and has been studying
  38. Psychology with particular attention to behaviourism and conversation
  39. ever since.
  40.  
  41. His chequered career spans back twelve years during which time he has
  42. trained people in the design and use of database management applications,
  43. been called upon to design and implement structured methodologies and has
  44. been a "good old fashioned" analyst programmer.
  45.  
  46. Matthew Probert is currently researching Artificial Intelligence with
  47. particular reference to the application of natural language processing,
  48. whereby a computer software package may decode written human language and
  49. respond to it in an intelligent manner. He is also monitoring the
  50. progress of facilitated communication amongst autistic and children with
  51. severe learning and challenging behaviour and hopes one day to be able to
  52. develope a computer based mechanism for true and reliable communication
  53. between autistic people and the rest of society.
  54.  
  55.  
  56. Matthew Probert can be contacted via
  57.      Servile Software
  58.      5 Longcroft Close
  59.      Basingstoke
  60.      Hampshire
  61.      RG21 8XG
  62.      England
  63.  
  64.      Telephone 01256 478576
  65.  
  66.                                     
  67.                                  PREFACE
  68.  
  69. In 1992, an English software house, Servile Software published a paper
  70. entitled "HOW TO C", which sought to introduce computer programmers to
  71. the C programming language. That paper was written by Matthew Probert. A
  72. follow up effort was "HOW TO CMORE", a document that was also published
  73. by Servile Software. Now those two documents have been amalgamated and
  74. thoroughly revamped to create this book. I have included loads of new
  75. source code that can be lifted directly out of the text.
  76.  
  77. All the program listings have been typed in to the Turbo C compiler,
  78. compiled and executed successfully before being imported into this
  79. document.
  80.  
  81. I hope you enjoy my work, and more I hope that you learn to program in C.
  82. It really is a great language, there can be no other language that gives
  83. the computer the opportunity to live up to the old saying;
  84.  
  85.  
  86. "To err is human, to make a complete balls up requires a computer!"
  87.  
  88.  
  89.  
  90. Warning!
  91.  
  92. This document is the result of over ten years experience as a software
  93. engineer. This document contains professional source code that is not
  94. intended for beginers.
  95.  
  96.  
  97.                                     
  98.                               INTRODUCTION
  99.  
  100.  
  101. The major distinguishing features of the C programming language are;
  102.  
  103.  ·    block-structured flow-control constructs (typical of most high-level
  104.       languages);
  105.  ·    freedom to manipulate basic machine objects (eg: bytes) and to refer
  106.       to them using any particular object view desired (typical of assembly-
  107.       languages);
  108.  ·    both high-level operations (eg: floating-point arithmetic) and low-
  109.       level operations (which map closely onto machine-language instructions,
  110.       thereby offering the means to code in an optimal, yet portable, manner).
  111.  
  112. This book sets out to describe the C programming language, as commonly
  113. found with compilers for the IBM PC, to enable a computer programmer with
  114. no previous knowledge of the C programming language to program in C using
  115. the IBM PC including the ROM facilities provided by the PC and facilities
  116. provided DOS.
  117.  
  118. It is assumed that the reader has access to a C compiler, and to the
  119. documentation that accompanies it regarding library functions.
  120.  
  121. The example programs were written with Borland's Turbo C, most of the non-
  122. standard facilities provided by Turbo C should be found in later releases
  123. of Microsoft C.
  124.  
  125.  
  126.  
  127. Differences Between the Various Versions of C
  128.  
  129. The original C (prior to the definitive book by K&R) defined the
  130. combination assignment operators (eg: +=, *=, etc.) backwards (ie: they
  131. were written =+, =*, etc.).  This caused terrible confusion when a
  132. statement such as
  133.  
  134.  x=-y;
  135. was compiled - it could have meant
  136.  
  137.  x = x - y;
  138. or
  139.  
  140.  x = (-y);
  141. Ritchie soon spotted this ambiguity and changed the language to have
  142. these operators written in the now-familiar manner (+=, *=, etc.).
  143.  
  144. The major variations, however, are between K&R C and ANSI C.  These can
  145. be summarized as follows:
  146.  
  147.  ·    introduction of function prototypes in declarations; change of
  148.       function definition preamble to match the style of prototypes;
  149.  ·    introduction of the ellipsis ("...") to show variable-length
  150.       function argument lists;
  151.  ·    introduction of the keyword `void' (for functions not returning a
  152.       value) and the type `void *' for generic pointer variables;
  153.  ·    addition of string-merging, token-pasting and stringizing functions
  154.       in the preprocessor;
  155.  ·    addition of trigraph translation in the preprocessor;
  156.  ·    addition of the `#pragma' directive and formalization of the
  157.       `declared()' pseudofunction in the preprocessor;
  158.  ·    introduction of multi-byte strings and characters to support non-
  159.       English languages;
  160.  ·    introduction of the `signed' keyword (to complement the `unsigned'
  161.       keyword when used in integer declarations) and the unary plus (`+')
  162.       operator.
  163.  
  164.  
  165.  
  166. C is a medium level language
  167.  
  168. The powerful facilities offered by C to allow manipulation of direct
  169. memory addresses and data, even down to the bit level, along with C's
  170. structured approach to programming cause C to be classified as a "medium
  171. level" programming language. It possesses fewer ready made facilities
  172. than a high level language, such as BASIC, but a higher level of
  173. structure than low level Assembler.
  174.  
  175.  
  176. Key words
  177.  
  178. The original C language as described in; "The C programming language", by
  179. Kernighan and Ritchie, provided 27 key words. To those 27 the ANSI
  180. standards committee on C have added five more. This confusingly results
  181. in two standards for the C language. However, the ANSI standard is
  182. quickly taking over from the old K & R standard.
  183.  
  184.  
  185. The 32 C key words are;
  186.  
  187. auto              double             int                struct
  188. break             else               long               switch
  189. case              enum               register           typedef
  190. char              extern             return             union
  191. const             float              short              unsigned
  192. continue          for                signed             void
  193. default           goto               sizeof             volatile
  194. do                if                 static             while
  195.  
  196. Some C compilers offer additional key words specific to the hardware
  197. environment that they operate on. You should be aware of your own C
  198. compilers additional key words. Most notably on the PC these are;
  199.  
  200.  
  201. near      far        huge
  202.  
  203.  
  204.  
  205. Structure
  206.  
  207. C programs are written in a structured manner. A collection of code
  208. blocks are created that call each other to comprise the complete program.
  209. As a structured language C provides various looping and testing commands
  210. such as;
  211.  
  212.  
  213.            do-while,  for, while, if
  214.  
  215. and the use of jumps, while provided for, are rarely used.
  216.  
  217. A C code block is contained within a pair of curly braces "{ }", and may
  218. be a complete procedure, in C terminology called a "function", or a
  219. subset of code within a function. For example the following is a code
  220. block. The statements within the curly braces are only executed upon
  221. satisfaction of the condition that "x < 10";
  222.  
  223.  
  224. if (x < 10)
  225. {
  226.      a = 1;
  227.      b = 0;
  228. }
  229.  
  230. while this, is a complete function code block containing a sub code block
  231. as a do-while loop;
  232.  
  233. int GET_X()
  234. {
  235.      int x;
  236.  
  237.      do
  238.      {
  239.           printf("\nEnter a number between 0 and 10 ");
  240.           scanf("%d",&x);
  241.      }
  242.      while(x < 0 || x > 10);
  243.      return(x);
  244. }
  245.  
  246. Notice how every statement line is terminated in a semicolon, unless that
  247. statement marks the start of a code block, in which case it is followed
  248. by a curly brace. C is a case sensitive but free flow language, spaces
  249. between commands are ignored, and therefore the semicolon delimiter is
  250. required to mark the end of the command line.
  251.  
  252. Having a freeflow structure the following commands are recognised as the
  253. same by the C compiler;
  254.  
  255.  
  256.      x = 0;
  257.      x      =0;
  258.      x=0;
  259.  
  260.  
  261. The general form of a C program is as follows;
  262.  
  263.      compiler preprocessor statements
  264.      global data declarations
  265.      
  266.      
  267.      
  268.      
  269.      return-type main(parameter list)
  270.      {
  271.           statements
  272.      }
  273.      
  274.      return-type f1(parameter list)
  275.      {
  276.           statements
  277.      }
  278.      
  279.      return-type f2(parameter list)
  280.      {
  281.           statements
  282.      }
  283.      .
  284.      .
  285.      .
  286.      return-type fn(parameter list)
  287.      {
  288.           statements
  289.      }
  290.      
  291.  
  292.  
  293. Comments
  294.  
  295. C allows comments to be included in the program. A comment line is
  296. defined by being enclosed within "/*" and "*/". Thus the following is a
  297. comment;
  298.  
  299.  
  300. /* This is a legitimate C comment line */
  301.  
  302.  
  303. Libraries
  304.  
  305. C programs are compiled and combined with library functions provided with
  306. the C compiler. These libraries are of generally standard functions, the
  307. functionality of which are defined in the ANSI standard of the C
  308. language, but are provided by the individual C compiler manufacturers to
  309. be machine dependant. Thus, the standard library function "printf()"
  310. provides the same facilities on a DEC VAX as on an IBM PC, although the
  311. actual machine language code in the library is quite different for each.
  312. The C programmer however, does not need to know about the internals of
  313. the libraries, only that each library function will behave in the same
  314. way on any computer.
  315.  
  316.  
  317.                                     
  318.                                DATA TYPES
  319.  
  320.  
  321. There are four basic types of data in the C language; character, integer,
  322. floating point, and valueless that are referred to by the C key words;
  323.  
  324. "char", "int", "float" and "void" respectively.
  325.  
  326. To the basic data types may be added the type modifiers; signed,
  327. unsigned, long and short to produce further data types. By default data
  328. types are assumed signed, and the signed modifier is rarely used, unless
  329. to overide a compiler switch defaulting a data type to unsigned.
  330.  
  331. The size of each data type varies from one hardware platform to another,
  332. but the least range of values that can be held is described in the ANSI
  333. standard as follows;
  334.  
  335.  
  336. Type                     Size                     Range
  337.                                                   
  338. char                     8                        -127 to 127
  339. unsigned char            8                        0 to 255
  340. int                      16                       -32767 to 32767
  341. unsigned int             16                       0 to 65535
  342. long int                 32                       -2147483647 to
  343.                                                   2147483647
  344. unsigned long int        32                       0 to 4294967295
  345. float                    32                       Six digit precision
  346. double                   64                       Ten digit precision
  347. long double              80                       Ten digit precision
  348.  
  349.  
  350. In practice, this means that the data type `char' is particularly
  351. suitable for storing flag type variables, such as status codes, which
  352. have a limited range of values. The `int' data type can be used, but if
  353. the range of values does not exceed 127 (or 255 for an unsigned char),
  354. then each declared variable would be wasting storage space.
  355.  
  356. Which real number data type to use: `float', `double' or `long double' is
  357. another tricky question. When numeric accuracy is required, for example
  358. in an accounting application, the instinct would be to use the `long
  359. double', but this requires at least 10 bytes of storage space for each
  360. variable. And real numbers are not as precise as integers anyway, so
  361. perhaps one should use integer data types instead and work around the
  362. problem. The data type `float' is worse than useless since its six digit
  363. precision is too inaccurate to be relied upon. Generally, then, you
  364. should use integer data types where ever possible, but if real numbers
  365. are required use at least a `double'.
  366.  
  367.  
  368. Declaring a variable
  369.  
  370. All variables in a C program must be declared before they can be used.
  371. The general form of a variable definition is;
  372.  
  373.  
  374.      type name;
  375.  
  376. So, for example to declare a variable "x", of data type "int" so that it
  377. may store a value in the range -32767 to 32767, you use the statement;
  378.  
  379.  
  380.      int x;
  381.  
  382. Character strings may be declared, which are in reality arrays of
  383. characters. They are declared as follows;
  384.  
  385.  
  386.      char name[number_of_elements];
  387.  
  388. So, to declare a string thirty characters long, and called `name' you
  389. would use the declaration;
  390.  
  391.  
  392.      char name[30];
  393.  
  394.  
  395. Arrays of other data types also may be declared in one, two or more
  396. dimensions in the same way. For example to declare a two dimensional
  397. array of integers;
  398.  
  399.  
  400.      int x[10][10];
  401.  
  402. The elements of this array are then accessed as;
  403.  
  404.      x[0][0]
  405.      x[0][1]
  406.      x[n][n]
  407.  
  408. There are three levels of access to variable; local, module and global. A
  409. variable declared within a code block is only known to the statements
  410. within that code block. A variable declared outside any function code
  411. blocks but prefixed with the storage modifier "static" is known only to
  412. the statements within that source module. A variable declared outside any
  413. functions and not prefixed with the static storage type modifier may be
  414. accessed by any statement within any source module of the program.
  415.  
  416.  
  417. For example;
  418.  
  419.      int error;
  420.      static int a;
  421.      
  422.      main()
  423.      {
  424.           int x;
  425.           int y;
  426.      
  427.      }
  428.      
  429.      funca()
  430.      {
  431.           /* Test variable 'a' for equality with 0 */
  432.           if (a == 0)
  433.           {
  434.                int b;
  435.                for(b = 0; b < 20; b++)
  436.                     printf("\nHello World");
  437.           }
  438.      
  439.      }
  440.      
  441.  
  442. In this example the variable `error' is accessible by all source code
  443. modules compiled together to form the finished program. The variable `a'
  444. is accessible by statements in both functions `main()' and `funca()', but
  445. is invisible to any other source module. Variables `x' and `y' are only
  446. accessible by statements within function `main()'. The variable `b' is
  447. only accessible by statements within the code block following the `if'
  448. statement.
  449.  
  450. If a second source module wished to access the variable `error' it would
  451. need to declare `error' as an `extern' global variable thus;
  452.  
  453.  
  454.      extern int error;
  455.      
  456.      funcb()
  457.      {
  458.      }
  459.      
  460. C will quite happily allow you, the programmer, to assign different data
  461. types to each other. For example, you may declare a variable to be of
  462. type `char' in which case a single byte of data will be allocated to
  463. store the variable. To this variable you can attempt to allocate larger
  464. values, for example;
  465.  
  466.  
  467.      main()
  468.      {
  469.      
  470.           x = 5000;
  471.      
  472.      }
  473.      
  474. In this example the variable `x' can only store a value between -127 and
  475. 128, so the figure 5000 will NOT be assigned to the variable `x'. Rather
  476. the value 136 will be assigned!
  477.  
  478.  
  479. Often you may wish to assign different data types to each other, and to
  480. prevent the compiler from warning you of a possible error you can use a
  481. cast to tell the compiler that you know what you're doing. A cast
  482. statement is a data type in parenthesis preceding a variable or
  483. expression;
  484.  
  485.  
  486.      main()
  487.      {
  488.           float x;
  489.           int y;
  490.      
  491.           x = 100 / 25;
  492.      
  493.           y = (int)x;
  494.      }
  495.  
  496. In this example the (int) cast tells the compiler to convert the value of
  497. the floating point variable x to an integer before assigning it to the
  498. variable y.
  499.  
  500.  
  501.  
  502. Formal parameters
  503.  
  504. A C function may receive parameters from a calling function. These
  505. parameters are declared as variables within the parentheses of the
  506. function name, thus;
  507.  
  508.  
  509.      int MULT(int x, int y)
  510.      {
  511.           /* Return parameter x multiplied by parameter y */
  512.           return(x * y);
  513.      }
  514.      
  515.      main()
  516.      {
  517.           int a;
  518.           int b;
  519.           int c;
  520.      
  521.           a = 5;
  522.           b = 7;
  523.           c = MULT(a,b);
  524.      
  525.           printf("%d multiplied by %d equals %d\n",a,b,c);
  526.      }
  527.      
  528.  
  529. Access modifiers
  530.  
  531. There are two access modifiers; `const' and `volatile'. A variable
  532. declared to be `const' may not be changed by the program, whereas a
  533. variable declared as type  as type `volatile' may be changed by the
  534. program. In addition, declaring a variable to be volatile prevents the C
  535. compiler from allocating the variable to a register, and reduces the
  536. optimization carried out on the variable.
  537.  
  538.  
  539.  
  540. Storage class types
  541. C provides four storage types; `extern', `static', `auto' and `register'.
  542.  
  543. The extern storage type is used to allow a source module within a C
  544. program to access a variable declared in another source module.
  545.  
  546. Static variables are only accessible within the code block that declared
  547. them, and additionally if the variable is local, rather than global, they
  548. retain their old value between subsequent calls to the code block.
  549.  
  550. Register variables are stored within CPU registers where ever possible,
  551. providing the fastest possible access to their values.
  552.  
  553. The auto type variable is only used with local variables, and declares
  554. the variable to retain its value locally only. Since this is the default
  555. for local variables the auto storage type is very rarely used.
  556.                                     
  557.                                     
  558.                                     
  559.                                 OPERATORS
  560.  
  561. Operators are tokens that cause a computation to occur when applied to
  562. variables. C provides the following operators;
  563.  
  564.  
  565. &                  Address
  566. *                  Indirection
  567. +                  Unary plus
  568. -                  Unary minus
  569. ~                  Bitwise compliment
  570. !                  Logical negation
  571. ++                 As a prefix;
  572.                    preincrement
  573.                    As a suffix;
  574.                    postincrement
  575. --                 As a prefix;
  576.                    predecrement
  577.                    As a suffix;
  578.                    postdecrement
  579. +                  Addition
  580. -                  Subtraction
  581. *                  Multiply
  582. /                  Divide
  583. %                  Remainder
  584. <<                 Shift left
  585. >>                 Shift right
  586. &                  Bitwise AND
  587. |                  Bitwise OR
  588. ^                  Bitwise XOR
  589. &&                 Logical AND
  590. ||                 Logical OR
  591. =                  Assignment
  592. *=                 Assign product
  593. /=                 Assign quotient
  594. %=                 Assign remainder
  595.                    (modulus)
  596. +=                 Assign sum
  597. -=                 Assign difference
  598. <<=                Assign left shift
  599. >>=                Assign right shift
  600. &=                 Assign bitwise AND
  601. |=                 Assign bitwise OR
  602. ^=                 Assign bitwise XOR
  603. <                  Less than
  604. >                  Greater than
  605. <=                 Less than or equal
  606.                    to
  607. >=                 Greater than or
  608.                    equal to
  609. ==                 Equal to
  610. !=                 Not equal to
  611. .                  Direct component
  612.                    selector
  613. ->                 Indirect component
  614.                    selector
  615. a ? x:y            "If a is true then
  616.                    x else y"
  617. []                 Define arrays
  618. ()                 Parenthesis
  619.                    isolate conditions
  620.                    and expressions
  621. ...                Ellipsis are used
  622.                    in formal
  623.                    parameter lists of
  624.                    function
  625.                    prototypes to show
  626.                    a variable number
  627.                    of
  628.                    parameters or
  629.                    parameters of
  630.                    varying types.
  631.  
  632. To illustrate some more commonly used operators consider the following
  633. short program;
  634.  
  635.  
  636.      main()
  637.      {
  638.           int a;
  639.           int b;
  640.           int c;
  641.           a = 5;           /* Assign a value of 5 to variable 'a' */
  642.           b = a / 2;       /* Assign the value of 'a' divided by two to
  643.      variable 'b' */
  644.           c = b * 2;       /* Assign the value of 'b' multiplied by two
  645.      to variable 'c' */
  646.      
  647.           if (a == c)     /* Test if 'a' holds the same value as 'c' */
  648.      
  649.                puts("Variable 'a' is an even number");
  650.           else
  651.                puts("Variable 'a' is an odd number");
  652.      }
  653.      
  654.      
  655. Normally when incrementing the value of a variable you would write
  656. something like;
  657.  
  658.           x = x + 1
  659.  
  660. C provides the incremental operator '++' as well so that you can write;
  661.  
  662.           x++
  663.  
  664. Similarly you can decrement the value of a variable using '--' as;
  665.  
  666.           x--
  667.  
  668. All the other mathematical operators may be used the same, so in a C
  669. program you can write in shorthand;
  670.  
  671.  
  672. NORMAL                               C
  673.                                      
  674. x = x + 1                            x++
  675. x = x - 1                            x--
  676. x = x * 2                            x *= 2
  677. x = x / y                            x /= y
  678. x = x % 5                            x %= 5
  679.  
  680. and so on.
  681.                                     
  682.                                     
  683.                                     
  684.                                 FUNCTIONS
  685.  
  686. Functions are the source code procedures that comprise a C program. They
  687. follow the general form;
  688.  
  689.      return_type function_name(parameter_list)
  690.      {
  691.           statements
  692.      }
  693.  
  694.  
  695. The return_type specifies the data type that will be returned by the
  696. function; char, int, double, void &c.
  697.  
  698. The code within a C function is invisible to any other C function, and
  699. jumps may not be made from one function into the middle of another,
  700. although functions may call other functions. Also, functions cannot be
  701. defined within functions, only within source modules.
  702.  
  703. Parameters may be passed to a function either by value, or by reference.
  704. If a parameter is passed by value, then only a copy of the current value
  705. of the parameter is passed to the function. A parameter passed by
  706. reference however, is a pointer to the actual parameter that may then be
  707. changed by the function.
  708.  
  709.  
  710. The following example passes two parameters by value to a function,
  711. funca(), which attempts to change the value of the variables passed to
  712. it. And then passes the same two parameters by reference to funcb() which
  713. also attempts to modify their values.
  714.  
  715.  
  716.      #include <stdio.h>
  717.      
  718.      int funca(int x, int y)
  719.      {
  720.           /* This function receives two parameters by value, x and y */
  721.      
  722.           x = x * 2;
  723.           y = y * 2;
  724.      
  725.           printf("\nValue of x in funca() %d value of y in funca()
  726.      %d",x,y);
  727.      
  728.           return(x);
  729.      }
  730.      
  731.      
  732.      int funcb(int *x, int *y)
  733.      {
  734.           /* This function receives two parameters by reference, x and y
  735.      */
  736.      
  737.           *x = *x * 2;
  738.           *y = *y * 2;
  739.      
  740.           printf("\nValue of x in funcb() %d value of y in funcb()
  741.      %d",*x,*y);
  742.      
  743.           return(*x);
  744.      }
  745.      
  746.      main()
  747.      {
  748.           int x;
  749.           int y;
  750.           int z;
  751.      
  752.           x = 5;
  753.           y = 7;
  754.      
  755.           z = funca(x,y);
  756.           z = funcb(&x,&y);
  757.      
  758.           printf("\nValue of x %d value of y %d value of z %d",x,y,z);
  759.      }
  760.      
  761.  
  762. Actually funcb() does not change the values of the parameters it
  763. receives.  Rather it changes the contents of the memory addresses pointed
  764. to by the received parameters. While funca() receives the values of
  765. variables `x' and `y' from function main(), funcb() receives the memory
  766. addresses of the variables `x' and `y' from function main().
  767.  
  768.  
  769.  
  770. Passing an array to a function
  771.  
  772. The following program passes an array to a function, funca(), which
  773. initialises the array elements;
  774.  
  775.      #include <stdio.h>
  776.      
  777.      void funca(int x[])
  778.      {
  779.           int n;
  780.      
  781.           for(n = 0; n < 100; n++)
  782.           x[n] = n;
  783.      }
  784.      
  785.      main()
  786.      {
  787.           int array[100];
  788.           int counter;
  789.      
  790.           funca(array);
  791.      
  792.           for(counter = 0; counter < 100; counter++)
  793.                printf("\nValue of element %d is
  794.      %d",counter,array[counter]);
  795.      }
  796.  
  797. The parameter of funca() `int x[]' is declared to be an array of any
  798. length.  This works because the compiler passes the address of the start
  799. of the array parameter to the function, rather than the value of the
  800. individual elements.  This does of course mean that the function can
  801. change the value of the array elements. To prevent a function from
  802. changing the values you can specify the parameter as type `const';
  803.  
  804.  
  805.      funca(const int x[])
  806.      {
  807.      }
  808.  
  809. This will then generate a compiler error at the line that attempts to
  810. write a value to the array. However, specifying a parameter to be const
  811. does not protect the parameter from indirect assignment as the following
  812. program illustrates;
  813.  
  814.  
  815.      #include <stdio.h>
  816.      
  817.      int funca(const int x[])
  818.      {
  819.           int *ptr;
  820.           int n;
  821.      
  822.           /* This line gives a 'suspicious pointer conversion warning' */
  823.           /* because x is a const pointer, and ptr is not */
  824.           ptr = x;
  825.      
  826.           for(n = 0; n < 100; n++)
  827.           {
  828.                *ptr = n;
  829.                ptr++;
  830.           }
  831.      }
  832.      
  833.      main()
  834.      {
  835.           int array[100];
  836.           int counter;
  837.      
  838.           funca(array);
  839.      
  840.           for(counter = 0; counter < 100; counter++)
  841.                printf("\nValue of element %d is
  842.      %d",counter,array[counter]);
  843.      }
  844.      
  845.  
  846.  
  847. Passing parameters to main()
  848.  
  849. C allows parameters to be passed from the operating system to the program
  850. when it starts executing through two parameters; argc and argv[], as
  851. follows;
  852.  
  853.  
  854.      #include <stdio.h>
  855.      
  856.      main(int argc, char *argv[])
  857.      {
  858.           int n;
  859.      
  860.           for(n = 0; n < argc; n++)
  861.           printf("\nParameter %d equals %s",n,argv[n]);
  862.      }
  863.      
  864.  
  865. Parameter argc holds the number of parameters passed to the program, and
  866. the array argv[] holds the addresses of each parameter passed. argv[0] is
  867. always the program name. This feature may be put to good use in
  868. applications that need to access system files. Consider the following
  869. scenario:
  870.  
  871. A simple database application stores its data in a single file called
  872. "data.dat". The application needs to be created so that it may be stored
  873. in any directory on either a floppy diskette or a hard disk, and executed
  874. both from within the host directory and through a DOS search path. To
  875. work correctly the application must always know where to find the data
  876. file; "data.dat". This is solved by assuming that the data file will be
  877. in the same directory as the executable module, a not unreasonable
  878. restriction to place upon the operator. The following code fragment then
  879. illustrates how an application may apply this algorithm into practice to
  880. be always able to locate a desired system file:
  881.  
  882.  
  883.      #include <string.h>
  884.      
  885.      char system_file_name[160];
  886.      
  887.      void main(int argc,char *argv[])
  888.      {
  889.           char *data_file = "DATA.DAT";
  890.           char *p;
  891.      
  892.           strcpy(system_file_name,argv[0]);
  893.           p = strstr(system_file_name,".EXE");
  894.           if (p == NULL)
  895.           {
  896.                /* The executable is a .COM file */
  897.              p = strstr(system_file_name,".COM");
  898.           }
  899.      
  900.           /* Now back track to the last '\' character in the file name */
  901.           while(*(p - 1) != '\\')
  902.                p--;
  903.      
  904.           strcpy(p,data_file);
  905.      }
  906.  
  907. In practice this code creates a string in system_file_name that is
  908. comprised of path\data.dat, so if for example the executable file is
  909. called "test.exe" and resides in the directory \borlandc, then
  910. system_file_name will be assigned with: \borlandc\data.dat
  911.  
  912.  
  913.  
  914. Returning from a function
  915.  
  916. The command `return' is used to return immediately from a function. If
  917. the function was declared with a return data type, then return should be
  918. used with a parameter of the same data type.
  919.  
  920.  
  921.  
  922. Function prototypes
  923.  
  924. Prototypes for functions allow the C compiler to check that the type of
  925. data being passed to and from functions is correct. This is very
  926. important to prevent data overflowing its allocated storage space into
  927. other variables areas.
  928.  
  929. A function prototype is placed at the beginning of the program, after any
  930. preprocessor commands, such as #include <stdio.h>, and before the
  931. declaration of any functions.
  932.                                     
  933.                            THE C PREPROCESSOR
  934.  
  935. C allows for commands to the compiler to be included in the source code.
  936. These commands are then called preprocessor commands and are defined by
  937. the ANSI standard to be;
  938.  
  939.  
  940.     #if
  941.     #ifdef
  942.     #ifndef
  943.     #else
  944.     #elif
  945.     #include
  946.     #define
  947.     #undef
  948.     #line
  949.     #error
  950.     #pragma
  951.  
  952. All preprocessor commands start with a hash symbol, "#", and must be on a
  953. line on their own (although comments may follow).
  954.  
  955.  
  956.  
  957. #define
  958.  
  959. The #define command specifies an identifier and a string that the
  960. compiler will substitute every time it comes accross the identifier
  961. within that source code module. For example;
  962.  
  963.  
  964. #define FALSE 0
  965. #define TRUE !FALSE
  966.  
  967. The compiler will replace any subsequent occurence of `FALSE' with `0'
  968. and any subsequent occurence of `TRUE' with `!0'. The substitution does
  969. NOT take place if the compiler finds that the identifier is enclosed by
  970. quotation marks, so
  971.  
  972.  
  973.      printf("TRUE");
  974.  
  975. would NOT be replaced, but
  976.  
  977.      printf("%d",FALSE);
  978.  
  979. would be.
  980.  
  981. The #define command also can be used to define macros that may include
  982. parameters. The parameters are best enclosed in parenthesis to ensure
  983. that correct substitution occurs.
  984.  
  985.  
  986. This example declares a macro `larger()' that accepts two parameters and
  987. returns the larger of the two;
  988.  
  989.      #include <stdio.h>
  990.      
  991.      #define larger(a,b) (a > b) ? (a) : (b)
  992.      
  993.      int main()
  994.      {
  995.           printf("\n%d is largest",larger(5,7));
  996.      
  997.      }
  998.  
  999.  
  1000. #error
  1001.  
  1002. The #error command causes the compiler to stop compilation and to display
  1003. the text following the #error command. For example;
  1004.  
  1005.  
  1006. #error REACHED MODULE B
  1007.  
  1008. will cause the compiler to stop compilation and display;
  1009.  
  1010.      REACHED MODULE B
  1011.  
  1012.  
  1013.  
  1014. #include
  1015.  
  1016. The #include command tells the compiler to read the contents of another
  1017. source file. The name of the source file must be enclosed either by
  1018. quotes or by angular brackets thus;
  1019.  
  1020.  
  1021.      #include "module2.c"
  1022.      #include <stdio.h>
  1023.  
  1024. Generally, if the file name is enclosed in angular brackets, then the
  1025. compiler will search for the file in a directory defined in the
  1026. compiler's setup. Whereas if the file name is enclosed in quotes then the
  1027. compiler will look for the file in the current directory.
  1028.  
  1029.  
  1030.  
  1031. #if, #else, #elif, #endif
  1032.  
  1033. The #if set of commands provide conditional compilation around the
  1034. general form;
  1035.  
  1036.      #if constant_expression
  1037.           statements
  1038.      #else
  1039.           statements
  1040.      #endif
  1041.  
  1042. #elif stands for '#else if' and follows the form;
  1043.  
  1044.      #if expression
  1045.           statements
  1046.      #elif expression
  1047.           statements
  1048.      #endif
  1049.  
  1050.  
  1051.  
  1052. #ifdef, #ifndef
  1053.  
  1054. These two commands stand for '#if defined' and '#if not defined'
  1055. respectively and follow the general form;
  1056.  
  1057.      #ifdef macro_name
  1058.           statements
  1059.      #else
  1060.           statements
  1061.      #endif
  1062.  
  1063.      #ifndef macro_name
  1064.           statements
  1065.      #else
  1066.           statements
  1067.      #endif
  1068.  
  1069. where 'macro_name' is an identifier declared by a #define statement.
  1070.  
  1071.  
  1072.  
  1073. #undef
  1074.  
  1075. Undefines a macro previously defined by #define.
  1076.  
  1077.  
  1078. #line
  1079.  
  1080. Changes the compiler declared global variables __LINE__ and __FILE__. The
  1081. general form of #line is;
  1082.  
  1083.      #line number "filename"
  1084.  
  1085. where number is inserted into the variable '__LINE__' and 'filename' is
  1086. assigned to '__FILE__'.
  1087.  
  1088.  
  1089. #pragma
  1090.  
  1091. This command is used to give compiler specific commands to the compiler.
  1092. The compiler's manual should give you full details of any valid options
  1093. to go with the particular implementation of #pragma that it supports.
  1094.                                     
  1095.                        PROGRAM CONTROL STATEMENTS
  1096.  
  1097. As with any computer language, C includes statements that test the
  1098. outcome of an expression. The outcome of the test is either TRUE or
  1099. FALSE. The C language defines a value of TRUE as non-zero, and FALSE as
  1100. zero.
  1101.  
  1102.  
  1103.  
  1104. Selection statements
  1105.  
  1106. The general purpose selection statement is "if" that follows the general
  1107. form;
  1108.  
  1109.           if (expression)
  1110.                statement
  1111.           else
  1112.                statement
  1113.  
  1114. Where "statement" may be a single statement, or a code block enclosed in
  1115. curly braces. The "else" is optional. If the result of the expression
  1116. equates to TRUE, then the statement(s) following the if() will be
  1117. evaluated.  Otherwise the statement(s) following the else, if there is
  1118. one, will be evaluated.
  1119.  
  1120.  
  1121. An alternative to the if....else combination is the ?: command that takes
  1122. the form;
  1123.  
  1124.  
  1125.            expression ? true_expression : false_expression
  1126.  
  1127. Where if the expression evaluates to TRUE, then the true_expression will
  1128. be evaluated, otherwise the false_expression will be evaluated. Thus we
  1129. get;
  1130.  
  1131.  
  1132.      #include <stdio.h>
  1133.      
  1134.      main()
  1135.      {
  1136.           int x;
  1137.      
  1138.           x = 6;
  1139.      
  1140.           printf("\nx is an %s number", x % 2 == 0 ? "even" : "odd");
  1141.      }
  1142.      
  1143. C also provides a multiple branch selection statement, switch, which
  1144. successively tests a value of an expression against a list of values and
  1145. branches program execution to the first match found. The general form of
  1146. switch is;
  1147.  
  1148.  
  1149.      switch (expression)
  1150.      {
  1151.           case value1 :  statements
  1152.                     break;
  1153.           case value2 :  statements
  1154.                     break;
  1155.           .
  1156.           .
  1157.           .
  1158.           .
  1159.           case valuen :  statements
  1160.                     break;
  1161.           default :      statements
  1162.      }
  1163.  
  1164. The break statement is optional, but if omitted, program execution will
  1165. continue down the list.
  1166.  
  1167.      #include <stdio.h>
  1168.      
  1169.      main()
  1170.      {
  1171.           int x;
  1172.      
  1173.           x = 6;
  1174.      
  1175.           switch(x)
  1176.           {
  1177.                case 0 : printf("\nx equals zero");
  1178.                      break;
  1179.                case 1 : printf("\nx equals one");
  1180.                      break;
  1181.                case 2 : printf("\nx equals two");
  1182.                      break;
  1183.                case 3 : printf("\nx equals three");
  1184.                      break;
  1185.                default : printf("\nx is larger than three");
  1186.           }
  1187.      }
  1188.  
  1189. Switch statements may be nested within one another. This is a
  1190. particularly useful feature for confusing people who read your source
  1191. code!
  1192.  
  1193.  
  1194. Iteration statements
  1195. C provides three looping or iteration statements; for, while, and do-
  1196. while. The for loop has the general form;
  1197.  
  1198.  
  1199.      for(initialization;condition;increment)
  1200.  
  1201. and is useful for counters such as in this example that displays the
  1202. entire ascii character set;
  1203.  
  1204.      #include <stdio.h>
  1205.      
  1206.      main()
  1207.      {
  1208.           int x;
  1209.      
  1210.           for(x = 32; x < 128; x++)
  1211.                printf("%d\t%c\t",x,x);
  1212.      }
  1213.      
  1214. An infinite for loop is also quite valid;
  1215.  
  1216.      for(;;)
  1217.      {
  1218.           statements
  1219.      }
  1220.  
  1221. Also, C allows empty statements. The following for loop removes leading
  1222. spaces from a string;
  1223.  
  1224.      for(; *str == ' '; str++)
  1225.           ;
  1226.  
  1227. Notice the lack of an initializer, and the empty statement following the
  1228. loop.
  1229.  
  1230. The while loop is somewhat simpler than the for loop and follows the
  1231. general form;
  1232.  
  1233.      while (condition)
  1234.           statements
  1235.  
  1236. The statement following the condition, or statements enclosed in curly
  1237. braces will be executed until the condition is FALSE. If the condition is
  1238. false before the loop commences, the loop statements will not be
  1239. executed. The do-while loop on the other hand is always executed at least
  1240. once. It takes the general form;
  1241.  
  1242.  
  1243.      do
  1244.      {
  1245.           statements
  1246.      }
  1247.      while(condition);
  1248.  
  1249.  
  1250. Jump statements
  1251.  
  1252. The "return" statement is used to return from a function to the calling
  1253. function. Depending upon the declared return data type of the function it
  1254. may or may not return a value;
  1255.  
  1256.  
  1257.      int MULT(int x, int y)
  1258.      {
  1259.           return(x * y);
  1260.      }
  1261.  
  1262. or;
  1263.  
  1264.      void FUNCA()
  1265.      {
  1266.           printf("\nHello World");
  1267.           return;
  1268.      }
  1269.  
  1270. The "break" statement is used to break out of a loop or from a switch
  1271. statement. In a loop it may be used to terminate the loop prematurely, as
  1272. shown here;
  1273.  
  1274.  
  1275.      #include <stdio.h>
  1276.      
  1277.      main()
  1278.      {
  1279.           int x;
  1280.      
  1281.           for(x = 0; x < 256; x++)
  1282.           {
  1283.                if (x == 100)
  1284.                     break;
  1285.      
  1286.                printf("%d\t",x);
  1287.           }
  1288.      }
  1289.  
  1290. In contrast to "break" is "continue", which forces the next iteration of
  1291. the loop to occur, effectively forcing program control back to the loop
  1292. statement.
  1293.  
  1294.  
  1295. C provides a function for terminating the program prematurely, "exit()".
  1296. Exit() may be used with a return value to pass back to the calling
  1297. program;
  1298.  
  1299.  
  1300.      exit(return_value);
  1301.  
  1302.  
  1303. More About ?:
  1304.  
  1305.  
  1306. A powerful, but often misunderstood feature of the C programming language
  1307. is ?:. This is an operator that acts upon a boolean expression, and
  1308. returns one of two values dependant upon the result of the expression;
  1309.  
  1310.  
  1311.      <boolean expression> ? <value for true> : <value for false>
  1312.  
  1313. It can be used almost anywhere, for example it was used in the binary
  1314. search demonstration program;
  1315.  
  1316.      printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");
  1317.  
  1318. Here it passes either "Not found" or "Located okay" to the printf()
  1319. function dependant upon the outcome of the boolean expression `result ==
  1320. 0'. Alternatively it can be used for assigning values to a variable;
  1321.  
  1322.      x = (a  == 0) ? (b) : (c);
  1323.  
  1324. Which will assign the value of b to variable x if a is equal to zero,
  1325. otherwise it will assign the value of c to variable x.
  1326.  
  1327. This example returns the name of the executing program, without any path
  1328. description;
  1329.  
  1330.      #include <stdio.h>
  1331.      #include <stddef.h>
  1332.      #include <string.h>
  1333.      
  1334.      char *progname(char *pathname)
  1335.      {
  1336.           /* Return name of running program */
  1337.           unsigned l;
  1338.           char *p;
  1339.           char *q;
  1340.           static char bnbuf[256];
  1341.      
  1342.           return pathname? p = strrchr (pathname, '\\'),
  1343.                      q = strrchr (pathname, '.'),
  1344.                      l = (q == NULL? strchr (pathname, '\0'): q)
  1345.                        - (p == NULL? p = pathname: ++p),
  1346.                      strncpy (bnbuf, p, l),
  1347.                      bnbuf[l] = '\0',
  1348.                      strlwr (bnbuf)
  1349.                   : NULL;
  1350.      }
  1351.      
  1352.      void main(int argc, char *argv[])
  1353.      {
  1354.           printf("\n%s",progname(argv[0]));
  1355.      }
  1356.      
  1357.      
  1358.  
  1359. Continue
  1360.  
  1361. The continue keyword forces control to jump to the test statement of the
  1362. innermost loop (while, do...while()). This can be useful for terminating
  1363. a loop gracefuly as this program that reads strings from a file until
  1364. there are no more illustrates;
  1365.  
  1366.  
  1367.      #include <stdio.h>
  1368.      
  1369.      void main()
  1370.      {
  1371.           FILE *fp;
  1372.           char *p;
  1373.           char buff[100];
  1374.      
  1375.           fp = fopen("data.txt","r");
  1376.           if (fp == NULL)
  1377.           {
  1378.                fprintf(stderr,"Unable to open file data.txt");
  1379.                exit(0);
  1380.           }
  1381.      
  1382.           do
  1383.           {
  1384.                p = fgets(buff,100,fp);
  1385.                if (p == NULL)
  1386.                     /* Force exit from loop */
  1387.                     continue;
  1388.                puts(p);
  1389.           }
  1390.           while(p);
  1391.      }
  1392.      
  1393. With a for() loop however, continue passes control back to the third
  1394. parameter!
  1395.                                     
  1396.                             INPUT AND OUTPUT
  1397.  
  1398.  
  1399.  
  1400. Input
  1401. Input to a C program may occur from the console, the standard input
  1402. device (unless otherwise redirected this is the console), from a file or
  1403. from a data port.
  1404.  
  1405. The general input command for reading data from the standard input stream
  1406. `stdin' is scanf(). Scanf() scans a series of input fields, one character
  1407. at a time. Each field is then formatted according to the appropriate
  1408. format specifier passed to the scanf() function as a parameter. This
  1409. field is then stored at the ADDRESS passed to scanf() following the
  1410. format specifiers list.
  1411.  
  1412. For example, the following program will read a single integer from the
  1413. stream stdin;
  1414.  
  1415.  
  1416.      main()
  1417.      {
  1418.           int x;
  1419.      
  1420.           scanf("%d",&x);
  1421.      }
  1422.  
  1423. Notice the address operator & prefixing the variable name `x' in the
  1424. scanf() parameter list. This is because scanf() stores values at
  1425. ADDRESSES rather than assigning values to variables directly.
  1426.  
  1427. The format string is a character string that may contain three types of
  1428. data:
  1429.  
  1430. whitespace characters (space, tab and newline), non-whitespace characters
  1431. (all ascii characters EXCEPT %) and format specifiers.
  1432.  
  1433. Format specifiers have the general form;
  1434.  
  1435.  
  1436.      %[*][width][h|l|L]type_character
  1437.  
  1438. After the % sign the format specifier is comprised of:
  1439.  
  1440.  an optional assignment suppression character, *, which suppresses
  1441.  assignment of the next input field.
  1442.  
  1443.  an optional width specifier, width, which declares the maximum number
  1444.  of characters to be read.
  1445.  
  1446.  an optional argument type modifier, h or l or L, where:
  1447.            h is a short integer
  1448.            l is a long
  1449.            L is a long double
  1450.  
  1451.      the data type character that is one of;
  1452.  
  1453.  
  1454. d      Decimal integer
  1455. D      Decimal long integer
  1456. o      Octal integer
  1457. O      Octal long integer
  1458. i      Decimal, octal or hexadecimal integer
  1459. I      Decimal, octal or hexadecimal long
  1460.        integer
  1461. u      Decimal unsigned integer
  1462. U      Decimal unsigned long integer
  1463. x      Hexadecimal integer
  1464. X      Hexadecimal long integer
  1465. e      Floating point
  1466. f      Floating point
  1467. g      Floating point
  1468. s      Character string
  1469. c      Character
  1470. %      % is stored
  1471.  
  1472. An example using scanf();
  1473.  
  1474.  
  1475.      #include <stdio.h>
  1476.      
  1477.      main()
  1478.      {
  1479.           char name[30];
  1480.           int age;
  1481.      
  1482.           printf("\nEnter your name and age ");
  1483.           scanf("%30s%d",name,&age);
  1484.           printf("\n%s %d",name,age);
  1485.      }
  1486.  
  1487. Notice the include line, "#include <stdio.h>", this is to tell the
  1488. compiler to also read the file stdio.h that contains the function
  1489. prototypes for scanf() and printf().
  1490.  
  1491.  
  1492. If you type in and run this sample program you will see that only one
  1493. name can be entered, that is you can't enter;
  1494.  
  1495.  
  1496.      JOHN SMITH
  1497.  
  1498. because scanf() detects the whitespace between "JOHN" and "SMITH" and
  1499. moves on to the next input field, which is age, and attempts to assign
  1500. the value "SMITH" to the age field! The limitations of scanf() as an
  1501. input function are obvious.
  1502.  
  1503. An alternative input function is gets() that reads a string of characters
  1504. from the stream stdin until a newline character is detected. The newline
  1505. character is replaced by a null (0 byte) in the target string. This
  1506. function has the advantage of allowing whitespace to be read in. The
  1507. following program is a modification to the earlier one, using gets()
  1508. instead of scanf().
  1509.  
  1510.  
  1511.      #include <stdio.h>
  1512.      #include <stdlib.h>
  1513.      #include <string.h>
  1514.      
  1515.      main()
  1516.      {
  1517.           char data[80];
  1518.           char *p;
  1519.           char name[30];
  1520.           int age;
  1521.      
  1522.           printf("\nEnter your name and age ");
  1523.           /* Read in a string of data */
  1524.           gets(data);
  1525.      
  1526.      
  1527.           /* P is a pointer to the last character in the input string */
  1528.           p = &data[strlen(data) - 1];
  1529.      
  1530.           /* Remove any trailing spaces by replacing them with null bytes
  1531.      */
  1532.           while(*p == ' '){
  1533.                *p = 0;
  1534.                p--;
  1535.           }
  1536.      
  1537.           /* Locate last space in the string */
  1538.           p = strrchr(data,' ');
  1539.      
  1540.           /* Read age from string and convert to an integer */
  1541.           age = atoi(p);
  1542.      
  1543.           /* Terminate data string at start of age field */
  1544.           *p = 0;
  1545.      
  1546.           /* Copy data string to name variable */
  1547.           strcpy(name,data);
  1548.      
  1549.           /* Display results */
  1550.           printf("\nName is %s age is %d",name,age);
  1551.      }
  1552.  
  1553.  
  1554. Output
  1555. The most common output function is printf() that is very similar to
  1556. scanf() except that it writes formatted data out to the standard output
  1557. stream stdout.  Printf() takes a list of output data fields and applies
  1558. format specifiers to each and outputs the result. The format specifiers
  1559. are the same as for scanf() except that flags may be added to the format
  1560. specifiers. These flags include;
  1561.  
  1562.  
  1563.      - Which left justifies the output padding to the right with spaces.
  1564.      + Which causes numbers to be prefixed by their sign
  1565.  
  1566. The width specifier is also slightly different for printf(). In its most
  1567. useful form is the precision specifier;
  1568.  
  1569.  
  1570.      width.precision
  1571.  
  1572. So, to print a floating point number to three decimal places you would
  1573. use;
  1574.  
  1575.      printf("%.3f",x);
  1576.  
  1577. And to pad a string with spaces to the right or truncate the string to
  1578. twenty characters if it is longer, to form a fixed twenty character
  1579. width;
  1580.  
  1581.      printf("%-20.20s",data);
  1582.  
  1583. Special character constants may appear in the printf() parameter list.
  1584. These are;
  1585.  
  1586. \n         Newline
  1587. \r         Carriage return
  1588. \t         Tab
  1589. \b         Sound the computer's bell
  1590. \f         Formfeed
  1591. \v         Vertical tab
  1592. \\         Backslash character
  1593. \'         Single quote
  1594. \"         Double quote
  1595. \?         Question mark
  1596. \O         Octal string
  1597. \x         Hexadecimal string
  1598.  
  1599. Thus, to display "Hello World", surrounded in quotation marks and
  1600. followed by a newline you would use;
  1601.  
  1602.      printf("\"Hello World\"\n");
  1603.  
  1604. The following program shows how a decimal integer may be displayed as a
  1605. decimal, hexadecimal or octal integer. The 04 following the % in the
  1606. printf() format tells the compiler to pad the displayed figure to a width
  1607. of at least four digits padded with leading zeroes if required.
  1608.  
  1609.  
  1610.      /* A simple decimal to hexadecimal and octal conversion program */
  1611.      
  1612.      #include <stdio.h>
  1613.      
  1614.      main()
  1615.      {
  1616.           int x;
  1617.      
  1618.           do
  1619.           {
  1620.                printf("\nEnter a number, or 0 to end ");
  1621.                scanf("%d",&x);
  1622.                printf("%04d  %04X  %04o",x,x,x);
  1623.           }
  1624.           while(x != 0);
  1625.      
  1626.      }
  1627.      
  1628. There are associated functions to printf() that you should be aware of.
  1629. fprintf() has the prototype;
  1630.  
  1631.      fprintf(FILE *fp,char *format[,argument,...]);
  1632.  
  1633. This variation on printf() simply sends the formatted output to the
  1634. specified file stream.
  1635.  
  1636. sprintf() has the function prototype;
  1637.  
  1638.      sprintf(char *s,char *format[,argument,...]);
  1639.  
  1640. and writes the formatted output to a string. You should take care using
  1641. sprintf() that the target string has been declared long enough to hold
  1642. the result of the sprintf() output. Otherwise other data will be
  1643. overwritten in memory.
  1644.  
  1645. An example of using sprintf() to copy multiple data to a string;
  1646.  
  1647.      #include<stdio.h>
  1648.      
  1649.      int main()
  1650.      {
  1651.           char buffer[50];
  1652.      
  1653.           sprintf(buffer,"7 * 5 == %d\n",7 * 5);
  1654.      
  1655.           puts(buffer);
  1656.      }
  1657.      
  1658.  
  1659. An alternative to printf() for outputting a simple string to the stream
  1660. stdout is puts(). This function sends a string to the stream stdout
  1661. followed by a newline character. It is faster than printf(), but far less
  1662. flexible. Instead of;
  1663.  
  1664.      printf("Hello World\n");
  1665.  
  1666. You can use;
  1667.  
  1668.      puts("Hello World");
  1669.  
  1670.  
  1671.  
  1672. Direct Console I/O
  1673. Data may be sent to, and read directly from the console (keyboard and
  1674. screen) using the direct console I/O functions. These functions are
  1675. prefixed `c'. The direct console I/O equivalent of printf() is then
  1676. cprintf(), and the equivalent of puts() is cputs(). Direct console I/O
  1677. functions differ from standard I?o functions:
  1678.  
  1679.   1. They do not make use of the predefined streams, and hence may not be
  1680.      redirected
  1681.   2. They are not portable across operating systems (for example you cant
  1682.      use direct console I/O functions in a Windows programme).
  1683.   3. They are faster than their standard I/O equivalents
  1684.   4. They may not work with all video modes (especially VESA display
  1685.      modes).
  1686.                                     
  1687.                                     
  1688.                                     
  1689.                                 POINTERS
  1690.  
  1691. A pointer is a variable that holds the memory address of an item of data.
  1692. Therefore it points to another item. A pointer is declared like an
  1693. ordinary variable, but its name is prefixed by `*', thus;
  1694.  
  1695.  
  1696.      char *p;
  1697.  
  1698. This declares the variable `p' to be a pointer to a character variable.
  1699. Pointers are very powerful, and similarly dangerous! If only because a
  1700. pointer can be inadvertently set to point to the code segment of a
  1701. program and then some value assigned to the address of the pointer!
  1702.  
  1703. The following program illustrates a simple, though fairly useless
  1704. application of a pointer;
  1705.  
  1706.  
  1707.      #include <stdio.h>
  1708.      
  1709.      main()
  1710.      {
  1711.           int a;
  1712.           int *x;
  1713.      
  1714.           /* x is a pointer to an integer data type */
  1715.      
  1716.           a = 100;
  1717.           x = &a;
  1718.      
  1719.           printf("\nVariable 'a' holds the value %d at memory address
  1720.      %p",a,x);
  1721.      }
  1722.  
  1723. Here is a more useful example of a pointer illustrating how because the
  1724. compiler knows the type of data pointed to by the pointer, when the
  1725. pointer is incremented it is incremented the correct number of bytes for
  1726. the data type.  In this case two bytes;
  1727.  
  1728.      #include <stdio.h>
  1729.      
  1730.      main()
  1731.      {
  1732.           int n;
  1733.           int a[25];
  1734.           int *x;
  1735.      
  1736.           /* x is a pointer to an integer data type */
  1737.      
  1738.           /* Assign x to point to array element zero */
  1739.           x = a;
  1740.      
  1741.           /* Assign values to the array */
  1742.           for(n = 0; n < 25; n++)
  1743.                a[n] = n;
  1744.      
  1745.      
  1746.           /* Now print out all array element values */
  1747.           for(n = 0; n < 25; n++ , x++)
  1748.                printf("\nElement %d holds %d",n,*x);
  1749.      }
  1750.      
  1751. To read or assign a value to the address held by a pointer you use the
  1752. indirection operator `*'. Thus in the above example, to print the value
  1753. at the memory address pointed to by variable x I have used `*x'.
  1754.  
  1755. Pointers may be incremented and decremented and have other mathematics
  1756. applied to them also. For example in the above program to move variable x
  1757. along the array one element at a time we put the statement `x++' in the
  1758. for loop. We could move x along two elements by saying `x += 2'. Notice
  1759. that this doesn't mean "add 2 bytes to the value of x", but rather it
  1760. means "add 2 of the pointer's data type size units to the value of x".
  1761.  
  1762. Pointers are used extensively in dynamic memory allocation. When a
  1763. program is running it is often necessary to temporarily allocate a block
  1764. of data, say a table, in memory. C provides the function malloc() for
  1765. this purpose that follows the general form;
  1766.  
  1767.  
  1768.      any pointer type = malloc(number_of_bytes);
  1769.  
  1770. malloc() actually returns a void pointer type, which means it can be any
  1771. type; integer, character, floating point or whatever. This example
  1772. allocates a table in memory for 1000 integers;
  1773.  
  1774.      #include <stdio.h>
  1775.      #include <stdlib.h>
  1776.      
  1777.      main()
  1778.      {
  1779.           int *x;
  1780.           int n;
  1781.      
  1782.           /* x is a pointer to an integer data type */
  1783.      
  1784.           /* Create a 1000 element table, sizeof() returns the compiler
  1785.      */
  1786.           /* specific number of bytes used to store an integer */
  1787.      
  1788.           x = malloc(1000 * sizeof(int));
  1789.      
  1790.      
  1791.           /* Check to see if the memory allocation succeeded */
  1792.           if (x == NULL)
  1793.           {
  1794.                printf("\nUnable to allocate a 1000 element integer
  1795.      table");
  1796.                exit(0);
  1797.           }
  1798.      
  1799.           /* Assign values to each table element */
  1800.           for(n = 0; n < 1000; n++)
  1801.           {
  1802.                *x = n;
  1803.                x++;
  1804.           }
  1805.      
  1806.           /* Return x to the start of the table */
  1807.           x -= 1000;
  1808.      
  1809.           /* Display the values in the table */
  1810.           for(n = 0; n < 1000; n++){
  1811.                printf("\nElement %d holds a value of %d",n,*x);
  1812.                x++;
  1813.           }
  1814.           /* Deallocate the block of memory now it's no longer required
  1815.      */
  1816.           free(x);
  1817.      }
  1818.      
  1819. Pointers are also extensively used with character arrays, called strings.
  1820. Since all C program strings are terminated by a zero byte we can count
  1821. the letters in a string using a pointer;
  1822.  
  1823.      #include <stdio.h>
  1824.      #include <string.h>
  1825.      
  1826.      main()
  1827.      {
  1828.           char *p;
  1829.           char text[100];
  1830.           int len;
  1831.      
  1832.           /* Initialise variable 'text' with some writing */
  1833.           strcpy(text,"This is a string of data");
  1834.      
  1835.           /* Set variable p to the start of variable text */
  1836.           p = text;
  1837.      
  1838.           /* Initialise variable len to zero */
  1839.           len = 0;
  1840.      
  1841.           /* Count the characters in variable text */
  1842.           while(*p)
  1843.           {
  1844.                len++;
  1845.                p++;
  1846.           }
  1847.      
  1848.           /* Display the result */
  1849.           printf("\nThe string of data has %d characters in it",len);
  1850.      }
  1851.      
  1852.  
  1853. The 8088/8086 group of CPUs, as used in the IBM PC, divide memory into
  1854. 64K segments. To address all 1Mb of memory a 20 bit number is used
  1855. comprised of an `offset' to and a 64K `segment'. The IBM PC uses special
  1856. registers called "segment registers" to record the segments of addresses.
  1857.  
  1858. This leads the C language on the IBM PC to have three new keywords; near,
  1859. far and huge.
  1860.  
  1861. near pointers are 16 bits wide and access only data within the current
  1862. segment.
  1863.  
  1864. far pointers are comprised of an offset and a segment address allowing
  1865. them to access data any where in memory, but arithmetic with far pointers
  1866. is fraught with danger! When a value is added or subtracted from a far
  1867. pointer it is only actualy the segment part of the pointer that is
  1868. affected, thus leading to the situation where a far pointer being
  1869. incremented wraps around on its own offset 64K segment.
  1870.  
  1871. huge pointers are a variation on the far pointer that can be successfuly
  1872. incremented and decremented through the entire 1Mb range since the
  1873. compiler generates code to amend the offset as applicable.
  1874.  
  1875. It will come as no surprise that code using near pointers is executed
  1876. faster than code using far pointers, which in turn is faster than code
  1877. using huge pointers.
  1878.  
  1879. to give a literal address to a far pointer IBM PC C compilers provide a
  1880. macro MK-FP() that has the prototype;
  1881.  
  1882.  
  1883.      void far *MK_FP(unsigned segment, unsigned offset);
  1884.  
  1885. For example, to create a far pointer to the start of video memory on a
  1886. colour IBM PC system you could use;
  1887.  
  1888.           screen = MK_FP(0xB800,0);
  1889.  
  1890. Two coressponding macros provided are;
  1891.  
  1892. FP_SEG() and FP_OFF()
  1893.  
  1894. Which return the segment and offset respectively of a far pointer. The
  1895. following example uses FP_SEG() and FP_OFF() to send segment and offset
  1896. addresses to a DOS call to create a new directory path;
  1897.  
  1898.  
  1899.      #include <dos.h>
  1900.      
  1901.      int makedir(char *path)
  1902.      {
  1903.           /* Make sub directory, returning non zero on success */
  1904.      
  1905.           union REGS inreg,outreg;
  1906.           struct SREGS segreg;
  1907.      
  1908.           inreg.h.ah = 0x39;
  1909.           segreg.ds = FP_SEG(path);
  1910.           inreg.x.dx = FP_OFF(path);
  1911.           intdosx(&inreg,&outreg,&segreg);
  1912.      
  1913.           return(1 - outreg.x.cflag);
  1914.      }
  1915.      
  1916.  
  1917. Finally, the CPU's segment registers can be read with the function
  1918. `segread()'. This is a function unique to C compilers for the the 8086
  1919. family of processors. segread() has the function prototype:
  1920.  
  1921.  
  1922.      void segread(struct SREGS *segp);
  1923.  
  1924. It returns the current values of the segment registers in the SREGS type
  1925. structure pointed to by the pointer `segp'. For example:
  1926.  
  1927.      #include <dos.h>
  1928.      
  1929.      main()
  1930.      {
  1931.           struct SREGS sregs;
  1932.      
  1933.           segread(&sregs);
  1934.           printf("\nCode segment is currently at %04X, Data segment is at
  1935.      %04X",sregs.cs,sregs.ds);
  1936.           printf("\nExtra segment is at %04X, Stack segment is at
  1937.      %04X",sregs.es,sregs.ss);
  1938.      }
  1939.                                     
  1940.                                STRUCTURES
  1941.  
  1942.  
  1943. C provides the means to group together variables under one name providing
  1944. a convenient means of keeping related information together and providing
  1945. a structured approach to data.
  1946.  
  1947. The general form for a structure definition is;
  1948.  
  1949.  
  1950.      typedef struct
  1951.      {
  1952.           variable_type variable_name;
  1953.           variable_type variable_name;
  1954.      }
  1955.      structure_name;
  1956.  
  1957.  
  1958. When accessing data files, with a fixed record structure, the use of a
  1959. structure variable becomes essential. The following example shows a
  1960. record structure for a very simple name and address file. It declares a
  1961. data structure called `data', and comprised of six fields; `name',
  1962. `address', `town', `county' , `post' and `telephone'.
  1963.  
  1964.  
  1965.      typedef struct
  1966.      {
  1967.           char name[30];
  1968.           char address[30];
  1969.           char town[30];
  1970.           char county[30];
  1971.           char post[12];
  1972.           char telephone[15];
  1973.      }
  1974.      data;
  1975.  
  1976. The structure 'data' may then be used in the program as a variable data
  1977. type for declaring variables;
  1978.  
  1979.      data record;
  1980.  
  1981. Thus declares a variable called 'record' to be of type 'data'.
  1982.  
  1983. The individual fields of the structure variable are accessed by the
  1984. general form;
  1985.  
  1986.      structure_variable.field_name;
  1987.  
  1988. Thus, the name field of the structure variable record is accessed with;
  1989.  
  1990.      record.name
  1991.  
  1992. There is no limit to the number of fields that may comprise a structure,
  1993. nor do the fields have to be of the same types, for example;
  1994.  
  1995.  
  1996.      typedef struct
  1997.      {
  1998.           char name[30];
  1999.           int age;
  2000.           char *notes;
  2001.      }
  2002.      dp;
  2003.  
  2004. Declares a structure 'dp' that is comprised of a character array field,
  2005. an integer field and a character pointer field.
  2006.  
  2007. A structure variable may be passed as a parameter by passing the address
  2008. of the variable as the parameter with the & operator thus;
  2009.  
  2010.      func(&my_struct)
  2011.  
  2012.  
  2013. The following is an example program that makes use of a structure to
  2014. provide the basic access to the data in a simple name and address file;
  2015.  
  2016.  
  2017. /* A VERY simple address book written in ANSI C */
  2018.  
  2019. #include <stdio.h>
  2020. #include <stdlib.h>
  2021. #include <io.h>
  2022. #include <string.h>
  2023. #include <fcntl.h>
  2024. #include <sys\stat.h>
  2025.  
  2026. /* num_lines is the number of screen display lines */
  2027. #define num_lines   25
  2028.  
  2029.  
  2030. typedef struct
  2031. {
  2032.      char name[30];
  2033.      char address[30];
  2034.      char town[30];
  2035.      char county[30];
  2036.      char post[12];
  2037.      char telephone[15];
  2038. }
  2039. data;
  2040.  
  2041. data record;
  2042. int handle;
  2043.  
  2044.  
  2045. /* Function prototypes */
  2046.  
  2047. void ADD_REC(void);
  2048. void CLS(void);
  2049. void DISPDATA(void);
  2050. void FATAL(char *);
  2051. void GETDATA(void);
  2052. void MENU(void);
  2053. void OPENDATA(void);
  2054. int SEARCH(void);
  2055.  
  2056. void CLS()
  2057. {
  2058.      int n;
  2059.  
  2060.      for(n = 0; n < num_lines; n++)
  2061.           puts("");
  2062. }
  2063.  
  2064. void FATAL(char *error)
  2065. {
  2066.      printf("\nFATAL ERROR: %s",error);
  2067.      exit(0);
  2068. }
  2069.  
  2070. void OPENDATA()
  2071. {
  2072.      /* Check for existence of data file and if not create it */
  2073.      /* otherwise open it for reading/writing at end of file */
  2074.  
  2075.      handle = open("address.dat",O_RDWR|O_APPEND,S_IWRITE);
  2076.  
  2077.      if (handle == -1)
  2078.      {
  2079.           handle = open("address.dat",O_RDWR|O_CREAT,S_IWRITE);
  2080.           if (handle == -1)
  2081.                FATAL("Unable to create data file");
  2082.      }
  2083. }
  2084.  
  2085. void GETDATA()
  2086. {
  2087.      /* Get address data from operator */
  2088.  
  2089.      CLS();
  2090.  
  2091.      printf("Name ");
  2092.      gets(record.name);
  2093.      printf("\nAddress ");
  2094.      gets(record.address);
  2095.      printf("\nTown ");
  2096.      gets(record.town);
  2097.      printf("\nCounty ");
  2098.      gets(record.county);
  2099.      printf("\nPost Code ");
  2100.      gets(record.post);
  2101.      printf("\nTelephone ");
  2102.      gets(record.telephone);
  2103. }
  2104.  
  2105. void DISPDATA()
  2106. {
  2107.      /* Display address data */
  2108.      char text[5];
  2109.  
  2110.      CLS();
  2111.  
  2112.      printf("Name %s",record.name);
  2113.      printf("\nAddress %s",record.address);
  2114.      printf("\nTown %s",record.town);
  2115.      printf("\nCounty %s",record.county);
  2116.      printf("\nPost Code %s",record.post);
  2117.      printf("\nTelephone %s\n\n",record.telephone);
  2118.  
  2119.      puts("Press RETURN to continue");
  2120.      gets(text);
  2121. }
  2122.  
  2123. void ADD_REC()
  2124. {
  2125.      /* Insert or append a new record to the data file */
  2126.      int result;
  2127.  
  2128.      result = write(handle,&record,sizeof(data));
  2129.  
  2130.      if (result == -1)
  2131.           FATAL("Unable to write to data file");
  2132. }
  2133.  
  2134. int SEARCH()
  2135. {
  2136.      char text[100];
  2137.      int result;
  2138.  
  2139.      printf("Enter data to search for ");
  2140.      gets(text);
  2141.      if (*text == 0)
  2142.           return(-1);
  2143.  
  2144.      /* Locate start of file */
  2145.      lseek(handle,0,SEEK_SET);
  2146.  
  2147.      do
  2148.      {
  2149.           /* Read record into memory */
  2150.           result = read(handle,&record,sizeof(data));
  2151.           if (result > 0)
  2152.           {
  2153.                /* Scan record for matching data */
  2154.                if (strstr(record.name,text) != NULL)
  2155.                     return(1);
  2156.                if (strstr(record.address,text) != NULL)
  2157.                     return(1);
  2158.                if (strstr(record.town,text) != NULL)
  2159.                     return(1);
  2160.                if (strstr(record.county,text) != NULL)
  2161.                     return(1);
  2162.                if (strstr(record.post,text) != NULL)
  2163.                     return(1);
  2164.                if (strstr(record.telephone,text) != NULL)
  2165.                     return(1);
  2166.           }
  2167.      }
  2168.      while(result > 0);
  2169.      return(0);
  2170. }
  2171.  
  2172. void MENU()
  2173. {
  2174.      int option;
  2175.      char text[10];
  2176.  
  2177.      do
  2178.      {
  2179.           CLS();
  2180.           puts("\n\t\t\tSelect Option");
  2181.           puts("\n\n\t\t\t1 Add new record");
  2182.           puts("\n\n\t\t\t2 Search for data");
  2183.           puts("\n\n\t\t\t3 Exit");
  2184.           puts("\n\n\n\n\n");
  2185.           gets(text);
  2186.           option = atoi(text);
  2187.  
  2188.           switch(option)
  2189.           {
  2190.                case 1 : GETDATA();
  2191.                      /* Go to end of file to append new record */
  2192.                      lseek(handle,0,SEEK_END);
  2193.                      ADD_REC();
  2194.                      break;
  2195.  
  2196.                case 2 : if (SEARCH())
  2197.                           DISPDATA();
  2198.                      else
  2199.                      {
  2200.                           puts("NOT FOUND!");
  2201.                           puts("Press RETURN to continue");
  2202.                           gets(text);
  2203.                      }
  2204.                      break;
  2205.  
  2206.                case 3 : break;
  2207.           }
  2208.      }
  2209.      while(option != 3);
  2210. }
  2211.  
  2212. void main()
  2213. {
  2214.      CLS();
  2215.      OPENDATA();
  2216.      MENU();
  2217. }
  2218.  
  2219. Bit Fields
  2220.  
  2221. C allows the inclusion of variables with a size of less than eight bits
  2222. to be included in structures. These variables are known as bit fields,
  2223. and may be any declared size from one bit upwards.
  2224.  
  2225. The general form for declaring a bit field is;
  2226.  
  2227.  
  2228.      type name : number_of_bits;
  2229.  
  2230.  
  2231. For example, to declare a set of status flags, each occupying one bit;
  2232.  
  2233. typedef struct
  2234. {
  2235.      unsigned carry  : 1;
  2236.      unsigned zero   : 1;
  2237.      unsigned over   : 1;
  2238.      unsigned parity : 1;
  2239. }
  2240. df;
  2241.  
  2242. df flags;
  2243.  
  2244. The variable `flags' then occupies only four bits in memory, and yet is
  2245. comprised of four variables that may be accessed like any other structure
  2246. field.
  2247.  
  2248.  
  2249.  
  2250. Uunions
  2251.  
  2252. Another facility provided by C for efficient use of available memory is
  2253. the union structure. A union structure is a collection of variables that
  2254. all share the same memory storage address. As such only one of the
  2255. variables is ever accessible at a time.
  2256.  
  2257. The general form of a union definition is;
  2258.  
  2259.  
  2260.      union name
  2261.      {
  2262.           type variable_name;
  2263.           type variable_name;
  2264.           .
  2265.           .
  2266.           .
  2267.           type variable_name;
  2268.      };
  2269.  
  2270. Thus, to declare a union structure for two integer variables;
  2271.  
  2272.      union data
  2273.      {
  2274.           int vara;
  2275.           int varb;
  2276.      };
  2277.  
  2278. and to declare a variable of type 'data';
  2279.  
  2280.      data my_var;
  2281.  
  2282. The variable 'my_var' is then comprised of the two variables 'vara' and
  2283. 'varb'  that are accessed like with any form of structure, eg;
  2284.  
  2285.      my_var.vara = 5;
  2286.  
  2287. Assigns a value of 5 to the variable 'vara' of union 'my_var'.
  2288.  
  2289.  
  2290. Enumerations
  2291.  
  2292. An enumeration assigns ascending integer values to a list of symbols. An
  2293. enumeration declaration takes the general form;
  2294.  
  2295.  
  2296.      enum name { enumeration list } variable_list;
  2297.  
  2298. Thus to define a symbol list of colours, you can say;
  2299.  
  2300.      enum COLOURS
  2301.      {
  2302.           BLACK,
  2303.           BLUE,
  2304.           GREEN,
  2305.           CYAN,
  2306.           RED,
  2307.           MAGENTA,
  2308.           BROWN,
  2309.           LIGHTGRAY,
  2310.           DARKGRAY,
  2311.           LIGHTBLUE,
  2312.           LIGHTGREEN,
  2313.           LIGHTCYAN,
  2314.           LIGHTRED,
  2315.           LIGHTMAGENTA,
  2316.           YELLOW,
  2317.           WHITE
  2318.      };
  2319.  
  2320. Then, the number zero may be referred to by the symbol BLACK, the number
  2321. one by the symbol BLUE, the number two by the symbol GREEN and so on.
  2322.  
  2323. The following program illustrates the use of an enumeration list to
  2324. symbolise integers;
  2325.  
  2326.      #include <stdio.h>
  2327.      
  2328.      enum COLOURS
  2329.      {
  2330.           BLACK,
  2331.           BLUE,
  2332.           GREEN,
  2333.           CYAN,
  2334.           RED,
  2335.           MAGENTA,
  2336.           BROWN,
  2337.           LIGHTGRAY,
  2338.           DARKGRAY,
  2339.           LIGHTBLUE,
  2340.           LIGHTGREEN,
  2341.           LIGHTCYAN,
  2342.           LIGHTRED,
  2343.           LIGHTMAGENTA,
  2344.           YELLOW,
  2345.           WHITE
  2346.      };
  2347.      
  2348.      
  2349.      void main()
  2350.      {
  2351.           int x;
  2352.      
  2353.           x = RED;
  2354.      
  2355.           printf("\nVariable 'x' holds %d",x);
  2356.      
  2357.      }
  2358.                                     
  2359.                                 FILE I/O
  2360.  
  2361.  
  2362. C provides buffered file streams for file access. Some C platforms, such
  2363. as Unix and DOS provide unbuffered file handles as well.
  2364.  
  2365.  
  2366.  
  2367. Buffered streams
  2368.  
  2369. Buffered streams are accessed through a variable of type 'file pointer'.
  2370. The data type FILE is defined in the header file stdio.h. Thus to declare
  2371. a file pointer;
  2372.  
  2373.      #include <stdio.h>
  2374.  
  2375.      FILE *ptr;
  2376.  
  2377. To open a stream C provides the function fopen(), which accepts two
  2378. parameters, the name of the file to be opened, and the access mode for
  2379. the file to be opened with. The access mode may be any one of;
  2380.  
  2381.  
  2382. Mode   Description
  2383.        
  2384. r      Open for reading
  2385. w      Create for writing, destroying any
  2386.        existing file
  2387. a      Open for append, creating a new file
  2388.        if it doesn't
  2389.         exist
  2390. r+      Open an existing file for reading
  2391.        and writing
  2392. w+      Create for reading and writing,
  2393.        destroying any
  2394.         existing file
  2395. a+      Open for append, creating a new file
  2396.        if it doesn't exist.
  2397.  
  2398.  
  2399. Optionaly either `b' or `t' may be appended for binary or text mode. If
  2400. neither is appended, then the file stream will be opened in the mode
  2401. described by the global variable _fmode. Data read or written from file
  2402. streams opened in text mode undergoes conversion, that is the characters
  2403. CR and LF are converted to CR LF pairs on writing, and the CR LF pair is
  2404. converted to a single LF on reading. File streams opened in binary mode
  2405. do not undergo conversion.
  2406.  
  2407.  
  2408. If fopen() fails to open the file, it returns a value of NULL (defined in
  2409. stdio.h) to the file pointer.
  2410.  
  2411. Thus, the following program will create a new file called "data.txt" and
  2412. open it for reading and writing;
  2413.  
  2414.      #include <stdio.h>
  2415.      
  2416.      void main()
  2417.      {
  2418.           FILE *fp;
  2419.      
  2420.           fp = fopen("data.txt","w+");
  2421.      
  2422.      }
  2423.  
  2424. To close a stream C provides the function fclose(), which accepts the
  2425. stream's file pointer as a parameter;
  2426.  
  2427.      fclose(fp);
  2428.  
  2429. If an error occurs in closing the file stream, fclose() returns non zero.
  2430.  
  2431. There are four basic functions for receiving and sending data to and from
  2432. streams; fgetc(), fputc(), fgets() and fputs().
  2433.  
  2434. fgetc() simply reads a single character from the specified input stream;
  2435.  
  2436.      char fgetc(FILE *fp);
  2437.  
  2438. Its opposite number is fputc(), which simply writes a single character to
  2439. the specified input stream;
  2440.  
  2441.      char fputc(char c, FILE *fp);
  2442.  
  2443. fgets() reads a string from the input stream;
  2444.  
  2445.      char *fgets(char s, int numbytes, FILE *fp);
  2446.  
  2447. It stops reading when either numbytes - 1 bytes have been read, or a
  2448. newline character is read in. A null terminating byte is appended to the
  2449. read string, s. If an error occurs, fgets() returns NULL.
  2450.  
  2451.  
  2452. fputs() writes a null terminated string to a stream;
  2453.  
  2454.      int fputs(char *s, FILE *fp);
  2455.  
  2456. Excepting fgets(), which returns a NULL pointer if an error occurs, all
  2457. the other functions described above return EOF (defined in stdio.h) if an
  2458. error occurs during the operation.
  2459.  
  2460.  
  2461. The following program creates a copy of the file "data.dat" as "data.old"
  2462. and illustrates the use of fopen(), fgetc(), fputc() and fclose();
  2463.  
  2464.  
  2465.      #include <stdio.h>
  2466.      
  2467.      int main()
  2468.      {
  2469.           FILE *in;
  2470.           FILE *out;
  2471.      
  2472.           in = fopen("data.dat","r");
  2473.      
  2474.           if (in == NULL)
  2475.           {
  2476.                puts("\nUnable to open file data.dat for reading");
  2477.                return(0);
  2478.           }
  2479.      
  2480.           out = fopen("data.old","w+");
  2481.      
  2482.           if (out == NULL)
  2483.           {
  2484.                puts("\nUnable to create file data.old");
  2485.                return(0);
  2486.           }
  2487.      
  2488.           /* Loop reading and writing one byte at a time until end-of-
  2489.      file */
  2490.           while(!feof(in))
  2491.                fputc(fgetc(in),out);
  2492.      
  2493.           /* Close the file streams */
  2494.           fclose(in);
  2495.           fclose(out);
  2496.      
  2497.           return(0);
  2498.      }
  2499.  
  2500. Example program using fputs() to copy text from stream stdin (usually
  2501. typed in at the keyboard) to a new file called "data.txt".
  2502.  
  2503.      #include <stdio.h>
  2504.      
  2505.      int main()
  2506.      {
  2507.           FILE *fp;
  2508.           char text[100];
  2509.      
  2510.           fp = fopen("data.txt","w+");
  2511.      
  2512.           do
  2513.           {
  2514.                gets(text);
  2515.                fputs(text,fp);
  2516.           }
  2517.           while(*text);
  2518.      
  2519.           fclose(fp);
  2520.      }
  2521.  
  2522.  
  2523. Random access using streams
  2524.  
  2525. Random file access for streams is provided for by the fseek() function
  2526. that has the following prototype;
  2527.  
  2528.      int fseek(FILE *fp, long numbytes, int fromwhere);
  2529.  
  2530. fseek() repositions a file pointer associated with a stream previously
  2531. opened by a call to fopen(). The file pointer is positioned `numbytes'
  2532. from the location `fromwhere', which may be the file beginning, the
  2533. current file pointer position, or the end of the file, symbolised by the
  2534. constants SEEK_SET, SEEK_CUR and SEEK_END respectively. If a call to
  2535. fseek() succeeds, a value of zero is returned.
  2536.  
  2537.  
  2538. Associated with fseek() is ftell(), which reports the current file
  2539. pointer position of a stream, and has the following function prototype;
  2540.  
  2541.  
  2542.      long int ftell(FILE *fp);
  2543.  
  2544. ftell() returns either the position of the file pointer, measured in
  2545. bytes from the start of the file, or -1 upon an error occurring.
  2546.  
  2547.  
  2548.  
  2549. Handles
  2550.  
  2551. File handles are opened with the open() function that has the prototype;
  2552.  
  2553.      int open(char *filename,int access[,unsigned mode]);
  2554.  
  2555. If open() is successful, the number of the file handle is returned.
  2556. Otherwise open() returns -1.
  2557.  
  2558. The access integer is comprised from bitwise oring together of the
  2559. symbolic constants declared in fcntl.h. These vary from compiler to
  2560. compiler but may be;
  2561.  
  2562.  
  2563.      O_APPEND       If set, the file pointer will be set to the end of
  2564. the
  2565.                     file prior to each write.
  2566.      O_CREAT        If the file does not exist it is created.
  2567.      O_TRUNC        Truncates the existing file to a length of zero
  2568. bytes.
  2569.      O_EXCL          Used with O_CREAT
  2570.      O_BINARY       Opens the file in binary mode
  2571.      O_TEXT         Opens file in text mode
  2572.  
  2573. The optional mode parameter is comprised by bitwise oring of the symbolic
  2574. constants defined in stat.h. These vary from C compiler to C compiler but
  2575. may be;
  2576.  
  2577.      S_IWRITE       Permission to write
  2578.      S_IREAD        Permission to read
  2579.  
  2580.  
  2581. Once a file handle has been assigned with open(), the file may be
  2582. accessed with read() and write().
  2583.  
  2584. Read() has the function prototype;
  2585.  
  2586.      int read(int handle, void *buf, unsigned num_bytes);
  2587.  
  2588. It attempts to read 'num_bytes' and returns the number of bytes actually
  2589. read from the file handle 'handle', and stores these bytes in the memory
  2590. block pointed to by 'buf'.
  2591.  
  2592. Write() is very similar to read() and has the same function prototype and
  2593. return values, but writes 'num_bytes' from the memory block pointed to by
  2594. 'buf'.
  2595.  
  2596. Files opened with open() are closed using close() that has the function
  2597. prototype;
  2598.  
  2599.      int close(int handle);
  2600.  
  2601. close() returns zero on success, and -1 if an error occurs trying to
  2602. close the handle.
  2603.  
  2604. Random access is provided by lseek(), which is very similar to fseek(),
  2605. except that it accepts an integer file handle as the first parameter
  2606. rather than a stream FILE pointer.
  2607.  
  2608. This example uses file handles to read data from stdin (usually the
  2609. keyboard) and copy the text to a new file called "data.txt".
  2610.  
  2611.      #include <io.h>
  2612.      #include <fcntl.h>
  2613.      #include <sys\stat.h>
  2614.      
  2615.      int main()
  2616.      {
  2617.           int handle;
  2618.           char text[100];
  2619.      
  2620.           handle = open("data.txt",O_RDWR|O_CREAT|O_TRUNC,S_IWRITE);
  2621.      
  2622.           do
  2623.           {
  2624.                gets(text);
  2625.                write(handle,&text,strlen(text));
  2626.           }
  2627.           while(*text);
  2628.      
  2629.           close(handle);
  2630.      }
  2631.  
  2632.  
  2633.  
  2634. Advanced File I/O
  2635.  
  2636. The ANSI standard on C defines file IO as by way of file streams, and
  2637. defines various functions for file access;
  2638.  
  2639.  
  2640. fopen() has the prototype;
  2641.  
  2642.      FILE *fopen(const char *name,const char *mode);
  2643.  
  2644. fopen() attempts to open a stream to a file name in a specified mode. If
  2645. successful a FILE type pointer is returned to the file stream, if the
  2646. call fails NULL is returned. The mode string can be one of the following;
  2647.  
  2648.  
  2649. Mode      Description
  2650.  r        Open for reading only
  2651.  w        Create for writing, overwriting any existing file with the same
  2652.           name.
  2653.  a        Open for append (writing at end of file) or create the file if
  2654. it
  2655.           does not exist.
  2656.  r+       Open an existing file for reading and writing.
  2657.  w+       Create a new file for reading and writing.
  2658.  a+       Open for append with read and write access.
  2659.  
  2660.  
  2661. fclose() is used to close a file stream previously opened by a call to
  2662. fopen(). It has the prototype;
  2663.  
  2664.      int fclose(FILE *fp);
  2665.  
  2666. When a call to fclose() is successful, all buffers to the stream are
  2667. flushed and a value of zero is returned. If the call fails fclose()
  2668. returns EOF.
  2669.  
  2670. Many host computers, and the IBM PC is no exception, use buffered file
  2671. access, that is when writing to a file stream the data is stored in
  2672. memory and only written to the stream when it exceeds a predefined number
  2673. of bytes. A power failure occurring before the data has been written to
  2674. the stream will result in the data never being written, so the function
  2675. fflush() can be called to force all pending data to be written. fflush()
  2676. has the prototype;
  2677.  
  2678.      int fflush(FILE *fp);
  2679.  
  2680. When a call to fflush() is successful, the buffers connected with the
  2681. stream are flushed and a value of zero is returned. On failure fflush()
  2682. returns EOF.
  2683.  
  2684. The location of the file pointer connected with a stream can be
  2685. determined with the function ftell(). ftell() has the prototype;
  2686.  
  2687.  
  2688.      long int ftell(FILE *fp);
  2689.  
  2690. and returns the offset of the file pointer in bytes from the start of the
  2691. file, or -1L if the call fails.
  2692.  
  2693. Similarly, you can move the file pointer to a new position with fseek().
  2694. fseek() has the prototype;
  2695.  
  2696.      int fseek(FILE *fp, long offset, int from_what_place);
  2697.  
  2698. fseek() attempts to move the file pointer, 'fp' 'offset' bytes from the
  2699. position 'from_what_place'. 'from_what_place' is predefined as one of;
  2700.  
  2701.      SEEK_SET       The file's beginning
  2702.      SEEK_CUR       The file pointer's current position
  2703.      SEEK_END       End of file
  2704.  
  2705. The offset may be a positive value to move the file pointer on through
  2706. the file, or negative to move backwards.
  2707.  
  2708. To move a file pointer quickly back to the start of a file, and clear any
  2709. references to errors that have occurred C provides the function rewind()
  2710. that has the prototype;
  2711.  
  2712.      void rewind(FILE *fp);
  2713.  
  2714. rewind(fp) is similar to fseek(fp,0L,SEEK_SET) in that they both set the
  2715. file pointer to the start of the file, but whereas fseek() clears the EOF
  2716. error marker, rewind() clears all error indicators.
  2717.  
  2718. Errors occurring with file functions can be checked with the function
  2719. ferror() that has the prototype;
  2720.  
  2721.      int ferror(FILE *fp);
  2722.  
  2723. ferror() returns a nonzero value if an error has occurred on the
  2724. specified stream. After checking ferror() and reporting any errors you
  2725. should clear the error indicators, and this can be done by a call to
  2726. clearerr() that has the prototype;
  2727.  
  2728.      void clearerr(FILE *fp);
  2729.  
  2730. The condition of reaching end of file (EOF) can be tested for with the
  2731. predefined macro feof() that has the prototype;
  2732.  
  2733.      int feof(FILE *fp);
  2734.  
  2735. feof() returns a nonzero value if an end of file error indicator was
  2736. detected on the specified file stream, and zero if the end of file has
  2737. not yet been reached.
  2738.  
  2739. Reading data from a file stream can be achieved using several functions;
  2740. A single character can be read with fgetc() that has the prototype;
  2741.  
  2742.  
  2743.      int fgetc(FILE *fp);
  2744.  
  2745. fgetc() returns either the character read converted to an integer, or EOF
  2746. if an error occurred.
  2747.  
  2748. Reading a string of data is achieved with fgets(). fgets() attempts to
  2749. read a string terminated by a newline character and of no more than a
  2750. specified number of bytes from the file stream. It has the prototype;
  2751.  
  2752.  
  2753.      char *fgets(char s, int n, FILE *fp);
  2754.  
  2755. A successful call to fgets() results in a string being stored in `s' that
  2756. is either terminated by a newline character, or that is `n' - 1
  2757. characters long, which ever came first. The newline character is retained
  2758. by fgets(), and a null bytes is appended to the string. If the call fails
  2759. a NULL pointer is returned.
  2760.  
  2761.  
  2762. Strings may be written to a stream using fputs() that has the prototype;
  2763.  
  2764.      int fputs(const char *s,FILE *fp);
  2765.  
  2766. fputs() writes all the characters in the string `s' to the stream `fp'
  2767. except the null terminating byte. On success, fputs() returns the last
  2768. character written, on failure it returns EOF.
  2769.  
  2770. To write a single character to a stream use fputc() that has the
  2771. prototype;
  2772.  
  2773.      int fputc(int c,FILE *fp);
  2774.  
  2775. If successful, fputc() returns the character written, otherwise it
  2776. returns EOF.
  2777.  
  2778. To read a large block of data, or a record from a stream you can use
  2779. fread() that has the prototype;
  2780.  
  2781.      size_t fread(void *ptr,size_t size, size_t n, FILE *fp);
  2782.  
  2783. fread() attempts to read 'n' items, each of length 'size' from the file
  2784. stream 'fp' into the block of memory pointed to by 'ptr'. To check the
  2785. success or failure status of fread() use ferror().
  2786.  
  2787. The sister function to fread() is fwrite() that has the prototype;
  2788.  
  2789.      size_t fwrite(const void *ptr,size_t size, size_t n,FILE *fp);
  2790.  
  2791. that writes 'n' items each of length 'size' from the memory area pointed
  2792. to by 'ptr' to the specified stream 'fp'.
  2793.  
  2794. Formatted input from a stream is achieved with fscanf() that has the
  2795. prototype;
  2796.  
  2797.      int fscanf(FILE *fp, const char *format[,address ...]);
  2798.  
  2799. fscanf() returns the number of fields successfully stored, and EOF on end
  2800. of file. This short example shows how fscanf() is quite useful for
  2801. reading numbers from a stream, but hopeless when it comes to strings!
  2802.  
  2803.      #include <stdio.h>
  2804.      
  2805.      void main()
  2806.      {
  2807.           FILE *fp;
  2808.           int a;
  2809.           int b;
  2810.           int c;
  2811.           int d;
  2812.           int e;
  2813.           char text[100];
  2814.      
  2815.           fp = fopen("data.txt","w+");
  2816.      
  2817.           if(!fp)
  2818.           {
  2819.                perror("Unable to create file");
  2820.                exit(0);
  2821.           }
  2822.      
  2823.           fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");
  2824.      
  2825.           fflush(fp);
  2826.      
  2827.           if (ferror(fp))
  2828.           {
  2829.                fputs("Error flushing stream",stderr);
  2830.                exit(1);
  2831.           }
  2832.      
  2833.           rewind(fp);
  2834.           if (ferror(fp))
  2835.           {
  2836.                fputs("Error rewind stream",stderr);
  2837.                exit(1);
  2838.           }
  2839.      
  2840.           fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);
  2841.           if (ferror(fp))
  2842.           {
  2843.                fputs("Error reading from stream",stderr);
  2844.                exit(1);
  2845.           }
  2846.      
  2847.           printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);
  2848.      }
  2849.      
  2850. As you can see from the example, fprintf() can be used to write formatted
  2851. data to a stream.
  2852.  
  2853. If you wish to store the position of a file pointer on a stream, and then
  2854. later restore it to the same position you can use the functions fgetpos()
  2855. and fsetpos(). fgetpos() reads the current location of the file pointer
  2856. and has the prototype;
  2857.  
  2858.      int fgetpos(FILE *fp, fpos_t *pos);
  2859.  
  2860. fsetpos() repositions the file pointer and has the prototype;
  2861.  
  2862.      int fsetpos(FILE *fp, const fpos_t *fpos);
  2863.  
  2864. fpos_t is defined in stdio.h.
  2865.  
  2866. These functions are more convenient than doing an ftell() followed by an
  2867. fseek().
  2868.  
  2869. An open stream can have a new file associated with it in place of the
  2870. existing file by using the function freopen() that has the prototype;
  2871.  
  2872.      FILE *freopen(const char *name,const char *mode,FILE *fp);
  2873.  
  2874. freopen() closes the existing stream and then attempts to reopens it with
  2875. the specified file name. This is useful for redirecting the predefined
  2876. streams stdin, stdout and stderr to a file or device.
  2877.  
  2878. For example; if you wish to redirect all output intended to stdout
  2879. (usually the host computer's display device) to a printer you might use;
  2880.  
  2881.      freopen("LPT1","w",stdout);
  2882.  
  2883. where LPT1 is the name of the printer device (on a PC host, LPT1 is the
  2884. name of the parallel port).
  2885.  
  2886.  
  2887. Predefined I/O Streams
  2888.  
  2889. There are three predefined I/O streams; stdin, stdout, and stderr. The
  2890. streams stdin and stdout default to the keyboard and display
  2891. respectively, but can be redirected on some hardware platforms, such as
  2892. the PC and under UNIX. The stream stderr defaults to the display and is
  2893. not usually redirected by the operator. Thus it can be used for the
  2894. display of error messages even when program output has been redirected,
  2895. such as with;
  2896.  
  2897.       fputs("Error message",stderr);
  2898.  
  2899. The functions printf() and puts(), output data to the stream stdout, and
  2900. can therefore be redirected by the operator of the program. scanf() and
  2901. gets() accept input from the stream stdin.
  2902.  
  2903. As an example of file i/o with the PC consider the following short
  2904. program that does a hex dump of a specified file to the predefined stream
  2905. stdout, which may be redirected to a file using;
  2906.  
  2907.           dump filename.ext > target.ext
  2908.  
  2909.      #include <stdio.h>
  2910.      #include <fcntl.h>
  2911.      #include <io.h>
  2912.      #include <string.h>
  2913.      
  2914.      main(int argc, char *argv[])
  2915.      {
  2916.           unsigned counter;
  2917.           unsigned char v1[20];
  2918.           int f1;
  2919.           int x;
  2920.           int n;
  2921.      
  2922.           if (argc != 2)
  2923.           {
  2924.                fputs("\nERROR: Syntax is dump f1\n",stderr);
  2925.                return(1);
  2926.           }
  2927.      
  2928.           f1 = open(argv[1],O_RDONLY);
  2929.      
  2930.           if (f1 == -1)
  2931.           {
  2932.                fprintf(stderr,"\nERROR: Unable to open %s\n",argv[1]);
  2933.                return(1);
  2934.           }
  2935.      
  2936.           fprintf(stdout,"\nDUMP OF FILE %s\n\n",strupr(argv[1]));
  2937.      
  2938.           counter = 0;
  2939.      
  2940.           while(1)
  2941.           {
  2942.                /* Set buffer to zero bytes */
  2943.                memset(v1,0,20);
  2944.      
  2945.                /* Read buffer from file */
  2946.                x = _read(f1,&v1,16);
  2947.      
  2948.                /* x will be 0 on EOF or -1 on error */
  2949.                if (x < 1)
  2950.                     break;
  2951.      
  2952.                /* Print file offset to stdout */
  2953.                fprintf(stdout,"%06d(%05x) ",counter,counter);
  2954.      
  2955.                counter += 16;
  2956.      
  2957.                /* print hex values of buffer to stdout */
  2958.                for(n = 0; n < 16; n++)
  2959.                     fprintf(stdout,"%02x ",v1[n]);
  2960.      
  2961.                /* Print ascii values of buffer to stdout */
  2962.                for(n = 0; n < 16; n++)
  2963.                {
  2964.                     if ((v1[n] > 31) && (v1[n] < 128))
  2965.                          fprintf(stdout,"%c",v1[n]);
  2966.                     else
  2967.                          fputs(".",stdout);
  2968.                }
  2969.      
  2970.                /* Finish the line with a new line */
  2971.                fputs("\n",stdout);
  2972.           }
  2973.      
  2974.           /* successful termination */
  2975.           return(0);
  2976.      }
  2977.                                     
  2978.                                  STRINGS
  2979.  
  2980. The C language has one of the most powerful string handling capabilities
  2981. of any general purpose computer language.
  2982.  
  2983. A string is a single dimension array of characters terminated by a zero
  2984. byte.
  2985.  
  2986. Strings may be initialised in two ways. Either in the source code where
  2987. they may be assigned a constant value, as in;
  2988.  
  2989.      int main()
  2990.      {
  2991.           char *p = "System 5";
  2992.           char name[] = "Test Program" ;
  2993.      }
  2994.  
  2995. or at run time by the function strcpy() that has the function prototype;
  2996.  
  2997.      char *strcpy(char *destination, char *source);
  2998.  
  2999. strcpy() copies the string pointed to by source into the location pointed
  3000. to by destination as in the following example;
  3001.  
  3002.  
  3003.      #include<stdio.h>
  3004.      
  3005.      int main()
  3006.      {
  3007.           char name[50];
  3008.      
  3009.           strcpy(name,"Servile Software");
  3010.      
  3011.           printf("\nName equals %s",name);
  3012.      }
  3013.  
  3014. C also allows direct access to each individual byte of the string, so the
  3015. following is quite permissible;
  3016.  
  3017.      #include<stdio.h>
  3018.      
  3019.      int main()
  3020.      {
  3021.           char name[50];
  3022.      
  3023.           strcpy(name,"Servile Software");
  3024.      
  3025.           printf("\nName equals %s",name);
  3026.      
  3027.           /* Replace first byte with lower case 's' */
  3028.           name[0] = 's';
  3029.      
  3030.           printf("\nName equals %s",name);
  3031.      }
  3032.      
  3033. The ANSI standard on the C programming language defines the following
  3034. functions for use with strings;
  3035.  
  3036. char *strcat(char *dest, char *source)            Appends string source
  3037. to the end of string destination.
  3038.  
  3039. char *strchr(char *s, int c)            Returns a pointer to the first
  3040. occurence of character 'c' within s.
  3041.  
  3042. int strcmp(char *s1, char *s2)               Compares strings s1 and s2
  3043. returning        < 0 if s1 is less than s2
  3044.                                                        == 0 if s1 and s2
  3045. are the same
  3046.                                                        > 0 if s1 is
  3047. greater than s2
  3048.  
  3049. int strcoll(char *s1, char *s2)              Compares strings s1 and s2
  3050.                     according to the collating sequence set by
  3051.                               setlocale() returning    < 0 if s1 is less
  3052. than s2
  3053.                                              == 0 if s1 and s2 are the
  3054. same
  3055.                                              > 0 if s1 is greater than s2
  3056.  
  3057. char *strcpy(char *dest, char *src)          Copies string src into
  3058. string dest.
  3059.  
  3060. unsigned strcspn(char *s1, char *s2)         Returns the length of string
  3061. s1 that consists entirely of characters not in
  3062. string s2.
  3063.  
  3064. unsigned strlen(char *s)                Returns the length of string s.
  3065.  
  3066. char *strncat(char *dest, char *src, unsigned len)     Copies at most
  3067. 'len' characters from string src into string dest.
  3068.  
  3069. int strncmp(char *s1, char *s2, unsigned len)     Compares at most 'len'
  3070. characters from
  3071.                               string s1 with string s2 returning      < 0
  3072. if s1 is less than s2
  3073.                                                   == 0 if s1 and s2 are
  3074. the same
  3075.                                                   > 0 if s1 is greater
  3076. than s2
  3077.  
  3078. char *strncpy(char *dest, char *src, unsigned len)     Copies 'len'
  3079. characters from string  src into string dest, truncating or
  3080.                               padding with zero bytes as required.
  3081.  
  3082. char *strpbrk(char *s1, char *s2)            Returns a pointer to the
  3083. first character in string s1 that occurs in
  3084.                               string s2.
  3085.  
  3086. char *strrchr(char *s, int c)           Returns a pointer to the last
  3087. occurence of 'c' within string s.
  3088.  
  3089. unsigned strspn(char *s1, char *s2)          Returns the length of the
  3090. initial segment of string s1 that consists
  3091.                               entirely of characters in string s2.
  3092.  
  3093. char *strstr(char *s1, char *s2)             Returns a pointer to the
  3094. first occurence of string s2 within string
  3095.                               s1, or NULL if string s2 is not found in
  3096. string s1.
  3097.  
  3098. char *strtok(char *s1, char *s2)             Returns a pointer to the
  3099. token found in string s1 that is defined by
  3100.                               delimiters in string s2. Returns NULLif no
  3101. tokens are found.
  3102.  
  3103. The ANSI standard also defines various functions for converting strings
  3104. into numbers and numbers into strings.
  3105.  
  3106. Some C compilers include functions to convert strings to upper and lower
  3107. case, but these functions are not defined in the ANSI standard. However,
  3108. the ANSI standard does define the functions; toupper() and tolower() that
  3109. return an
  3110.  
  3111. integer parameter converted to upper and lowercase respectively. By using
  3112. these functions we can create our own ANSI compatible versions;
  3113.  
  3114.  
  3115.      #include<stdio.h>
  3116.      
  3117.      void strupr(char *source)
  3118.      {
  3119.           char *p;
  3120.      
  3121.           p = source;
  3122.           while(*p)
  3123.           {
  3124.                *p = toupper(*p);
  3125.                p++;
  3126.           }
  3127.      }
  3128.      
  3129.      void strlwr(char *source)
  3130.      {
  3131.           char *p;
  3132.      
  3133.           p = source;
  3134.           while(*p)
  3135.           {
  3136.                *p = tolower(*p);
  3137.                p++;
  3138.           }
  3139.      }
  3140.  
  3141.  
  3142.      int main()
  3143.      {
  3144.           char name[50];
  3145.      
  3146.           strcpy(name,"Servile Software");
  3147.      
  3148.           printf("\nName equals %s",name);
  3149.      
  3150.           strupr(name);
  3151.      
  3152.           printf("\nName equals %s",name);
  3153.      
  3154.           strlwr(name);
  3155.      
  3156.           printf("\nName equals %s",name);
  3157.      }
  3158.  
  3159. C does not impose a maximum length that a string may be, unlike other
  3160. computer languages. However, some CPUs impose restrictions on the maximum
  3161. size a block of memory can be. For example, the 8088 family of CPUs, as
  3162. used by the IBM PC, impose a limit of 64K bytes on a segment of memory.
  3163.  
  3164. An example program to reverse all the characters in a string.
  3165.  
  3166.      #include <stdio.h>
  3167.      #include <string.h>
  3168.      
  3169.      char *strrev(char *s)
  3170.      {
  3171.           /* Reverses the order of all characters in a string except the
  3172.      null */
  3173.           /* terminating byte */
  3174.      
  3175.           char *start;
  3176.           char *end;
  3177.           char tmp;
  3178.      
  3179.           /* Set pointer 'end' to last character in string */
  3180.           end = s + strlen(s) - 1;
  3181.      
  3182.           /* Preserve pointer to start of string */
  3183.           start = s;
  3184.      
  3185.           /* Swop characters */
  3186.           while(end >= s)
  3187.           {
  3188.                tmp = *end;
  3189.                *end = *s;
  3190.                *s = tmp;
  3191.                end--;
  3192.                s++;
  3193.           }
  3194.           return(start);
  3195.      }
  3196.      
  3197.      
  3198.      main()
  3199.      {
  3200.           char text[100];
  3201.           char *p;
  3202.      
  3203.           strcpy(text,"This is a string of data");
  3204.      
  3205.           p = strrev(text);
  3206.      
  3207.           printf("\n%s",p);
  3208.      }
  3209.      
  3210.  
  3211. Strtok()
  3212.  
  3213. The function strtok() is a very powerful standard C feature for
  3214. extracting substrings from within a single string. It is used where the
  3215. substrings are separated by known delimiters, such as commas in the
  3216. following example;
  3217.  
  3218.      #include <stdio.h>
  3219.      #include <string.h>
  3220.      
  3221.      main()
  3222.      {
  3223.           char data[50];
  3224.           char *p;
  3225.      
  3226.           strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");
  3227.      
  3228.           p = strtok(data,",");
  3229.           while(p)
  3230.           {
  3231.                puts(p);
  3232.                p = strtok(NULL,",");
  3233.           };
  3234.      }
  3235.      
  3236. Or this program can be written with a for() loop thus;
  3237.  
  3238.      #include <stdio.h>
  3239.      #include <string.h>
  3240.      
  3241.      main()
  3242.      {
  3243.           char data[50];
  3244.           char *p;
  3245.      
  3246.           strcpy(data,"RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO,VIOLET");
  3247.      
  3248.           for(strtok(data,","); p; p = strtok(NULL,","))
  3249.           {
  3250.                puts(p);
  3251.           };
  3252.      }
  3253.      
  3254. They both compile to the same code but follow different programming
  3255. styles.
  3256.  
  3257. Initially, you call strtok() with the name of the string variable to be
  3258. parsed, and a second string that contains the known delimiters. Strtok()
  3259. then returns a pointer to the start of the first substring and replaces
  3260. the first token with a zero delimiter. Subsequent calls to strtok() can
  3261. be made in a loop passing NULL as the string to be parsed, and strtok()
  3262. will return the subsequent substrings.
  3263.  
  3264.  
  3265. Since strtok() can accept many delimiter characters in the second
  3266. parameter string we can use it as the basis of a simple word counting
  3267. program;
  3268.  
  3269.      #include <stdio.h>
  3270.      #include <stdlib.h>
  3271.      #include <string.h>
  3272.      
  3273.      void main(int argc, char *argv[])
  3274.      {
  3275.           FILE *fp;
  3276.           char buffer[256];
  3277.           char *p;
  3278.           long count;
  3279.      
  3280.           if (argc != 2)
  3281.           {
  3282.                fputs("\nERROR: Usage is wordcnt <file>\n",stderr);
  3283.                exit(0);
  3284.           }
  3285.      
  3286.           /* Open file for reading */
  3287.           fp = fopen(argv[1],"r");
  3288.      
  3289.           /* Check the open was okay */
  3290.           if (!fp)
  3291.           {
  3292.                fputs("\nERROR: Cannot open source file\n",stderr);
  3293.                exit(0);
  3294.           }
  3295.      
  3296.           /* Initialise word count */
  3297.           count = 0;
  3298.      
  3299.           do
  3300.           {
  3301.                /* Read a line of data from the file */
  3302.                fgets(buffer,255,fp);
  3303.      
  3304.                /* check for an error in the read or EOF */
  3305.                if (ferror(fp) || feof(fp))
  3306.                     continue;
  3307.      
  3308.                /* count words in received line */
  3309.                /* Words are defined as separated by the characters */
  3310.                /* \t(tab) \n(newline) , ; : . ! ? ( ) - and [space] */
  3311.                p = strtok(buffer,"\t\n,;:.!?()- ");
  3312.                while(p)
  3313.                {
  3314.                     count++;
  3315.                     p = strtok(NULL,"\t\n,;:.!?()- ");
  3316.                }
  3317.           }
  3318.           while(!ferror(fp) && !feof(fp));
  3319.      
  3320.           /* Finished reading. Was it due to an error? */
  3321.           if (ferror(fp))
  3322.           {
  3323.                fputs("\nERROR: Reading source file\n",stderr);
  3324.                fclose(fp);
  3325.                exit(0);
  3326.           }
  3327.      
  3328.           /* Reading finished due to EOF, quite valid so print count */
  3329.           printf("\nFile %s contains %ld words\n",argv[1],count);
  3330.           fclose(fp);
  3331.      }
  3332.      
  3333.  
  3334.  
  3335.  
  3336. Converting Numbers To And From Strings
  3337.  
  3338. All C compilers provide a facility for converting numbers to strings.
  3339. This being sprintf(). However, as happens sprintf() is a multi-purpose
  3340. function that is therefore large and slow. The following function ITOS()
  3341. accepts two parameters, the first being a signed integer and the second
  3342. being a pointer to a character string. It then copies the integer into
  3343. the memory pointed to by the character pointer. As with sprintf() ITOS()
  3344. does not check that the target string is long enough to accept the result
  3345. of the conversion. You should then ensure that the target string is long
  3346. enough.
  3347.  
  3348. Example function for copying a signed integer into a string;
  3349.  
  3350.      void ITOS(long x, char *ptr)
  3351.      {
  3352.           /* Convert a signed decimal integer to a string */
  3353.      
  3354.           long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,
  3355.      1000, 100, 10, 1 };
  3356.           int n;
  3357.      
  3358.           /* Check sign */
  3359.           if (x < 0)
  3360.           {
  3361.                *ptr++ = '-';
  3362.                /* Convert x to absolute */
  3363.                x = 0 - x;
  3364.           }
  3365.      
  3366.           for(n = 0; n < 9; n++)
  3367.           {
  3368.                if (x > pt[n])
  3369.                {
  3370.                     *ptr++ = '0' + x / pt[n];
  3371.                     x %= pt[n];
  3372.                }
  3373.           }
  3374.           return;
  3375.      }
  3376.      
  3377. To convert a string to a floating point number, C provides two functions;
  3378. atof() and strtod(). atof() has the prototype;
  3379.  
  3380.      double atof(const char *s);
  3381.  
  3382. strtod has the prototype;
  3383.  
  3384.      double strtod(const char *s,char **endptr);
  3385.  
  3386. Both functions scan the string and convert it as far as they can, until
  3387. they come across a character they don't understand. The difference
  3388. between the two functions is that if strtod() is passed a character
  3389. pointer for parameter `endptr', it sets that pointer to the first
  3390. character in the string that terminated the conversion. Because of its
  3391. better error reporting, by way of endptr, strtod() is often preferred to
  3392. atof().
  3393.  
  3394. To convert a string to an integer use atoi() that has the prototype;
  3395.  
  3396.  
  3397.      int atoi(const char *s);
  3398.  
  3399. atoi() does not check for an overflow, and the results are undefined!
  3400.  
  3401. atol() is a similar function but returns a long. Alternatively, you can
  3402. use strtol() and stroul() instead that have better error checking.
  3403.  
  3404.                                     
  3405.                               TEXT HANDLING
  3406.  
  3407. Human languages write information down as `text'. This is comprised of
  3408. words, figures and punctuation. The words being made up of upper case and
  3409. lower case letters. Processing text with a computer is a commonly
  3410. required task, and yet quite a difficult one.
  3411.  
  3412. The ANSI C definitions include string processing functions that are by
  3413. their nature sensitive to case. That is the letter `A' is seen as
  3414. distinct from the letter `a'. This is the first problem that must be
  3415. overcome by the programmer. Fortunately both Borland's Turbo C compilers
  3416. and Microsoft's C compilers include case insensitive forms of the string
  3417. functions.
  3418.  
  3419. stricmp() for example is the case insensitive form of strcmp(), and
  3420. strnicmp() is the case insensitive form of strncmp().
  3421.  
  3422. If you are concerned about writing portable code, then you must restrict
  3423. yourself to the ANSI C functions, and write your own case insensitive
  3424. functions using the tools provided.
  3425.  
  3426. Here is a simple implementation of a case insensitive version of
  3427. strstr().  The function simply makes a copy of the parameter strings,
  3428. converts those copies both to upper case and then does a standard
  3429. strstr() on the copies.  The offset of the target string within the
  3430. source string will be the same for the copy as the original, and so it
  3431. can be returned relative to the parameter string.
  3432.  
  3433.  
  3434.      char *stristr(char *s1, char *s2)
  3435.      {
  3436.           char c1[1000];
  3437.           char c2[1000];
  3438.           char *p;
  3439.      
  3440.           strcpy(c1,s1);
  3441.           strcpy(c2,s2);
  3442.      
  3443.           strupr(c1);
  3444.           strupr(c2);
  3445.      
  3446.           p = strstr(c1,c2);
  3447.           if (p)
  3448.                return s1 + (p - c1);
  3449.           return NULL;
  3450.      }
  3451.      
  3452. This function scans a string, s1 looking for the word held in s2. The
  3453. word must be a complete word, not simply a character pattern, for the
  3454. function to return true. It makes use of the stristr() function described
  3455. previously.
  3456.  
  3457.      int word_in(char *s1,char *s2)
  3458.      {
  3459.           /* return non-zero if s2 occurs as a word in s1 */
  3460.           char *p;
  3461.           char *q;
  3462.           int ok;
  3463.      
  3464.           ok = 0;
  3465.           q = s1;
  3466.      
  3467.           do
  3468.           {
  3469.                /* Locate character occurence s2 in s1 */
  3470.                p = stristr(q,s2);
  3471.                if (p)
  3472.                {
  3473.                     /* Found */
  3474.                     ok = 1;
  3475.      
  3476.                     if (p > s1)
  3477.                     {
  3478.                          /* Check previous character */
  3479.                          if (*(p - 1) >= 'A' && *(p - 1) <= 'z')
  3480.                               ok = 0;
  3481.                     }
  3482.      
  3483.                     /* Move p to end of character set */
  3484.                     p += strlen(s2);
  3485.                     if (*p)
  3486.                     {
  3487.                          /* Check character following */
  3488.                          if (*p >= 'A' && *p <= 'z')
  3489.                               ok = 0;
  3490.                     }
  3491.                }
  3492.                q = p;
  3493.           }
  3494.           while(p && !ok);
  3495.           return ok;
  3496.      }
  3497.  
  3498.  
  3499. Some more useful functions for dealing with text are truncstr() that
  3500. truncates a string;
  3501.  
  3502.      void truncstr(char *p,int num)
  3503.      {
  3504.           /* Truncate string by losing last num characters */
  3505.           if (num < strlen(p))
  3506.                p[strlen(p) - num] = 0;
  3507.      }
  3508.      
  3509. trim() that removes trailing spaces from the end of a string;
  3510.  
  3511.      void trim(char *text)
  3512.      {
  3513.           /* remove trailing spaces */
  3514.           char *p;
  3515.      
  3516.           p = &text[strlen(text) - 1];
  3517.           while(*p == 32 && p >= text)
  3518.                *p-- = 0;
  3519.      }
  3520.      
  3521. strlench() that changes the length of a string by adding or deleting
  3522. characters;
  3523.  
  3524.      void strlench(char *p,int num)
  3525.      {
  3526.           /* Change length of string by adding or deleting characters */
  3527.      
  3528.           if (num > 0)
  3529.                memmove(p + num,p,strlen(p) + 1);
  3530.           else
  3531.           {
  3532.                num = 0 - num;
  3533.                memmove(p,p + num,strlen(p) + 1);
  3534.           }
  3535.      }
  3536.      
  3537. strins() that inserts a string into another string;
  3538.  
  3539.      void strins(char *p, char *q)
  3540.      {
  3541.           /* Insert string q into p */
  3542.           strlench(p,strlen(q));
  3543.           strncpy(p,q,strlen(q));
  3544.      }
  3545.      
  3546. strchg() that replaces all occurences of one sub-string with another
  3547. within a target string;
  3548.  
  3549.      void strchg(char *data, char *s1, char *s2)
  3550.      {
  3551.           /* Replace all occurences of s1 with s2 */
  3552.           char *p;
  3553.           char changed;
  3554.      
  3555.           do
  3556.           {
  3557.                changed = 0;
  3558.                p = strstr(data,s1);
  3559.                if (p)
  3560.                {
  3561.                     /* Delete original string */
  3562.                     strlench(p,0 - strlen(s1));
  3563.      
  3564.                     /* Insert replacement string */
  3565.                     strins(p,s2);
  3566.                     changed = 1;
  3567.                }
  3568.           }
  3569.           while(changed);
  3570.      }
  3571.                                     
  3572.                                   TIME
  3573.  
  3574. C provides a function, time(), which reads the computer's system clock to
  3575. return the system time as a number of seconds since midnight on January
  3576. the first, 1970. However, this value can be converted to a useful string
  3577. by the function ctime() as illustrated in the following example;
  3578.  
  3579.      #include <stdio.h>
  3580.      #include <time.h>
  3581.      
  3582.      int main()
  3583.      {
  3584.           /* Structure to hold time, as defined in time.h  */
  3585.           time_t t;
  3586.      
  3587.           /* Get system date and time from computer */
  3588.           t = time(NULL);
  3589.           printf("Today's date and time: %s\n",ctime(&t));
  3590.      }
  3591.      
  3592. The string returned by ctime() is comprised of seven fields;
  3593.  
  3594.      Day of the week,
  3595.      Month of the year,
  3596.      Date of the day of the month,
  3597.      hour,
  3598.      minutes,
  3599.      seconds,
  3600.      century of the year
  3601.  
  3602. terminated by a newline character and null terminating byte. Since the
  3603. fields always occupy the same width, slicing operations can be carried
  3604. out on the string with ease. The following program defines a structure
  3605. `time' and a function gettime() that extracts the hours, minutes and
  3606. seconds of the current time and places them in the structure;
  3607.  
  3608.  
  3609.      #include <stdio.h>
  3610.      #include <time.h>
  3611.      
  3612.      struct time
  3613.      {
  3614.           int ti_min;         /* Minutes */
  3615.           int ti_hour;        /* Hours */
  3616.           int ti_sec;         /* Seconds */
  3617.      };
  3618.      
  3619.      void gettime(struct time *now)
  3620.      {
  3621.           time_t t;
  3622.           char temp[26];
  3623.           char *ts;
  3624.      
  3625.           /* Get system date and time from computer */
  3626.           t = time(NULL);
  3627.      
  3628.           /* Translate dat and time into a string */
  3629.           strcpy(temp,ctime(&t));
  3630.      
  3631.           /* Copy out just time part of string */
  3632.           temp[19] = 0;
  3633.           ts = &temp[11];
  3634.      
  3635.           /* Scan time string and copy into time structure */
  3636.           sscanf(ts,"%2d:%2d:%2d",&now->ti_hour,&now->ti_min,&now-
  3637.      >ti_sec);
  3638.      }
  3639.      
  3640.      int main()
  3641.      {
  3642.           struct time now;
  3643.      
  3644.           gettime(&now);
  3645.      
  3646.           printf("\nThe time is
  3647.      %02d:%02d:%02d",now.ti_hour,now.ti_min,now.ti_sec);
  3648.      
  3649.      }
  3650.  
  3651. The ANSI standard on C does actually provide a function ready made to
  3652. convert the value returned by time() into a structure;
  3653.  
  3654.      #include <stdio.h>
  3655.      #include <time.h>
  3656.      
  3657.      int main()
  3658.      {
  3659.           time_t t;
  3660.           struct tm *tb;
  3661.      
  3662.           /* Get time into t */
  3663.           t = time(NULL);
  3664.      
  3665.           /* Convert time value t into structure pointed to by tb */
  3666.           tb = localtime(&t);
  3667.      
  3668.           printf("\nTime is %02d:%02d:%02d",tb->tm_hour,tb->tm_min,tb-
  3669.      >tm_sec);
  3670.      }
  3671.      
  3672. The structure 'tm' is defined in time.h as;
  3673.  
  3674.      struct tm
  3675.      {
  3676.           int tm_sec;
  3677.           int tm_min;
  3678.           int tm_hour;
  3679.           int tm_mday;
  3680.           int tm_mon;
  3681.           int tm_year;
  3682.           int tm_wday;
  3683.           int tm_yday;
  3684.           int tm_isdst;
  3685.      };
  3686.  
  3687.  
  3688. Timers
  3689. Often a program must determine the date and time from the host computer's
  3690. non-volatile RAM. There are several time functions provided by the ANSI
  3691. standard on C that allow a program to retrieve, from the host computer,
  3692. the current date and time;
  3693.  
  3694. time() returns the number of seconds that have elapsed since midnight on
  3695. January the 1st 1970. It has the prototype;
  3696.  
  3697.      time_t time(time_t *timer);
  3698.  
  3699. time() fills in the time_t variable sent as a parameter and returns the
  3700. same value. You can call time() with a NULL parameter and just collect
  3701. the return value thus;
  3702.  
  3703.      #include <time.h>
  3704.      
  3705.      void main()
  3706.      {
  3707.           time_t now;
  3708.      
  3709.           now = time(NULL);
  3710.      }
  3711.      
  3712. asctime() converts a time block to a twenty six character string of the
  3713. format;
  3714.  
  3715.                 Wed Oct 14 10:23:45 1992\n\0
  3716.  
  3717. asctime() has the prototype;
  3718.  
  3719.                char *asctime(const struct tm *tblock);
  3720.  
  3721. ctime() converts a time value (as returned by time()) into a twenty six
  3722. chracter string of the same format as asctime(). For example;
  3723.  
  3724.      #include <stdio.h>
  3725.      #include <time.h>
  3726.      
  3727.      void main()
  3728.      {
  3729.           time_t now;
  3730.           char date[30];
  3731.      
  3732.           now = time(NULL);
  3733.           strcpy(date,ctime(&now));
  3734.      }
  3735.      
  3736. difftime() returns the difference, in seconds, between two values (as
  3737. returned by time()). This can be useful for testing the elapsed time
  3738. between two events, the time a function takes to execute, and for
  3739. creating consistent delays that are irrelevant of the host computer.
  3740.  
  3741. An example delay program;
  3742.  
  3743.      #include <stdio.h>
  3744.      #include <time.h>
  3745.      
  3746.      
  3747.      void DELAY(int period)
  3748.      {
  3749.           time_t start;
  3750.      
  3751.           start = time(NULL);
  3752.           while(time(NULL) < start + period)
  3753.                ;
  3754.      }
  3755.      
  3756.      void main()
  3757.      {
  3758.           printf("\nStarting delay now....(please wait 5 seconds)");
  3759.      
  3760.           DELAY(5);
  3761.      
  3762.           puts("\nOkay, I've finished!");
  3763.      }
  3764.  
  3765. gmtime() converts a local time value (as returned by time()) to the GMT
  3766. time and stores it in a time block. This function depends upon the global
  3767. variable timezone being set.
  3768.  
  3769.  
  3770. The time block is a predefined structure (declared in time.h) as follows;
  3771.  
  3772.      struct tm
  3773.      {
  3774.           int tm_sec;
  3775.           int tm_min;
  3776.           int tm_hour;
  3777.           int tm_mday;
  3778.           int tm_mon;
  3779.           int tm_year;
  3780.           int tm_wday;
  3781.           int tm_yday;
  3782.           int tm_isdst;
  3783.      };
  3784.  
  3785. tm_mday records the day of the month, ranging from 1 to 31; tm_wday is
  3786. the day of the week with Sunday being represented by 0; the year is
  3787. recorded less 1900; tm_isdst is a flag to show whether daylight saving
  3788. time is in effect. The actual names of the structure and its elements may
  3789. vary from compiler to compiler, but the structure should be the same in
  3790. essence.
  3791.  
  3792. mktime() converts a time block to a calendar format. It follows the
  3793. prototype;
  3794.  
  3795.                 time_t mktime(struct tm *t);
  3796.  
  3797. The following example allows entry of a date, and uses mktime() to
  3798. calculate the day of the week appropriate to that date. Only dates from
  3799. the first of January 1970 are recognisable by the time functions.
  3800.  
  3801.      #include <stdio.h>
  3802.      #include <time.h>
  3803.      #include <string.h>
  3804.      
  3805.      void main()
  3806.      {
  3807.           struct tm tsruct;
  3808.           int okay;
  3809.           char data[100];
  3810.           char *p;
  3811.           char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
  3812.      "Thursday", "Friday", "Saturday" ,
  3813.                     "prior to 1970, thus not known" };
  3814.           do
  3815.           {
  3816.                okay = 0;
  3817.                printf("\nEnter a date as dd/mm/yy ");
  3818.                p = fgets(data,8,stdin);
  3819.                p = strtok(data,"/");
  3820.      
  3821.                if (p != NULL)
  3822.                     tsruct.tm_mday = atoi(p);
  3823.                else
  3824.                     continue;
  3825.      
  3826.                p = strtok(NULL,"/");
  3827.                if (p != NULL)
  3828.                     tsruct.tm_mon = atoi(p);
  3829.                else
  3830.                     continue;
  3831.      
  3832.                p = strtok(NULL,"/");
  3833.      
  3834.                if (p != NULL)
  3835.                     tsruct.tm_year = atoi(p);
  3836.                else
  3837.                     continue;
  3838.                okay = 1;
  3839.           }
  3840.           while(!okay);
  3841.      
  3842.           tsruct.tm_hour = 0;
  3843.           tsruct.tm_min = 0;
  3844.           tsruct.tm_sec = 1;
  3845.           tsruct.tm_isdst = -1;
  3846.      
  3847.           /* Now get day of the week */
  3848.           if (mktime(&tsruct) == -1)
  3849.           tsruct.tm_wday = 7;
  3850.      
  3851.           printf("That was %s\n",wday[tsruct.tm_wday]);
  3852.      }
  3853.      
  3854. mktime() also makes the neccessary adjustments for values out of range,
  3855. this can be utilised for discovering what the date will be in n number of
  3856. days time thus;
  3857.  
  3858.  
  3859.      #include <stdio.h>
  3860.      #include <time.h>
  3861.      #include <string.h>
  3862.      
  3863.      void main()
  3864.      {
  3865.           struct tm *tsruct;
  3866.           time_t today;
  3867.      
  3868.           today = time(NULL);
  3869.           tsruct = localtime(&today);
  3870.      
  3871.           tsruct->tm_mday += 10;
  3872.           mktime(tsruct);
  3873.      
  3874.           printf("In ten days it will be %02d/%02d/%2d\n", tsruct-
  3875.      >tm_mday,tsruct->tm_mon + 1,tsruct->tm_year);
  3876.      
  3877.      }
  3878.      
  3879.  
  3880. This program uses Julian Dates to decide any day of the week since the
  3881. 1st of October 1582 when the Gregorian calendar was introduced.
  3882.  
  3883.      char *WDAY(int day, int month, int year)
  3884.      {
  3885.           /* Returns a pointer to a string representing the day of the
  3886.      week */
  3887.      
  3888.           static char *cday[] = { "Saturday","Sunday","Monday","Tuesday",
  3889.      "Wednesday","Thursday","Friday" };
  3890.           double yy;
  3891.           double yt;
  3892.           double j;
  3893.           int y1;
  3894.           int y4;
  3895.           int x;
  3896.      
  3897.           yy = year / 100;
  3898.           y1 = (int)(yy);
  3899.           yt = year / 400;
  3900.           y4 = (int)(yt);
  3901.           x = 0;
  3902.      
  3903.           if (month < 3)
  3904.           {
  3905.                year--;
  3906.                x = 12;
  3907.           }
  3908.      
  3909.           j = day + (int)(365.25*year);
  3910.      
  3911.           j += (int)(30.6001 * (month + 1 + x)) - y1 + y4;
  3912.      
  3913.           if (yy == y1 && yt != y4 && month < 3)
  3914.                j++;
  3915.      
  3916.           j = 1 + j - 7 * (int)(j/7);
  3917.      
  3918.           if (j > 6)
  3919.                j -= 7;
  3920.      
  3921.           return(cday[j]);
  3922.      }
  3923.      
  3924.      
  3925. With time() and difftime() we can create a timer for testing the
  3926. execution times of functions thus;
  3927.  
  3928.      #include <stdio.h>
  3929.      #include <time.h>
  3930.      
  3931.      main()
  3932.      {
  3933.           time_t now;
  3934.           time_t then;
  3935.           double elapsed;
  3936.      
  3937.           int n;
  3938.      
  3939.           now = time(NULL);
  3940.      
  3941.           /* This loop is adjustable for multiple passes */
  3942.           for(n = 0; n < 5000; n++)
  3943.                /* Call the function to test */
  3944.                func();
  3945.      
  3946.           then = time(NULL);
  3947.      
  3948.           elapsed = difftime(then,now);
  3949.           printf("\nElapsed seconds==%lf\n",elapsed);
  3950.      }
  3951.      
  3952. By way of time() and ctime() the current system date and time can be
  3953. retrieved from the host computer thus;
  3954.  
  3955.      #include <stdio.h>
  3956.      #include <time.h>
  3957.      
  3958.      main()
  3959.      {
  3960.           time_t now;
  3961.           char *date;
  3962.           int n;
  3963.      
  3964.           /* Get system time */
  3965.           now = time(NULL);
  3966.      
  3967.           /* Convert system time to a string */
  3968.           date = ctime(&now);
  3969.      
  3970.           /*Display system time */
  3971.           printf("\nIt is %s",date);
  3972.      }
  3973.      
  3974. time_t is a type defined in time.h as the type of variable returned by
  3975. time(). This type may vary from compiler to compiler, and therefore is
  3976. represented by the type "time_t".
  3977.                                     
  3978.                               HEADER FILES
  3979.  
  3980. Function prototypes for library functions supplied with the C compiler,
  3981. and standard macros are declared in header files.
  3982.  
  3983. The ANSI standard on the C programming language lists the following
  3984. header files;
  3985.  
  3986. Header file    Description
  3987.                
  3988. assert.h       Defines the assert debugging macro
  3989. ctype.h        Character classification and
  3990.                conversion macros
  3991. errno.h        Constant mnemonics for error codes
  3992. float.h        Defines implementation specific
  3993.                macros for dealing with floating
  3994.                point mathematics
  3995. limits.h       Defines implementation specific
  3996.                limits on type values
  3997. locale.h       Country specific parameters
  3998. math.h         Prototypes for mathematics functions
  3999. setjmp.h       Defines typedef and functions for
  4000.                setjmp/longjmp
  4001. signal.h       Constants and declarations for use by
  4002.                signal() and raise()
  4003. stdarg.h       Macros for dealing with argument
  4004.                lists
  4005. stddef.h       Common data types and macros
  4006. stdio.h        Types and macros required for
  4007.                standard I/O
  4008. stdlib.h       Prototypes of commonly used functions
  4009.                and miscellany
  4010. string.h       String manipulation function
  4011.                prototypes
  4012. time.h         Structures for time conversion
  4013.                routines
  4014.                                     
  4015.                                 DEBUGGING
  4016.  
  4017. The ANSI standard on C includes a macro function for debugging called
  4018. assert(). This expands to an if() statement, which if it returns true
  4019. terminates the program and outputs to the standard error stream a message
  4020. comprised of:
  4021.  
  4022.  
  4023.      Assertion failed: <test>, file <module>, line <line number>
  4024.      Abnormal program termination
  4025. For example, the following program accidentally assigns a zero value to a
  4026. pointer!
  4027.  
  4028.      #include <stdio.h>
  4029.      #include <assert.h>
  4030.      
  4031.      main()
  4032.      {
  4033.           /* Demonstration of assert */
  4034.      
  4035.           int *ptr;
  4036.           int x;
  4037.      
  4038.           x = 0;
  4039.      
  4040.           /* Whoops! error in this line! */
  4041.           ptr = x;
  4042.      
  4043.           assert(ptr != NULL);
  4044.      }
  4045.      
  4046. When run, this program terminates with the message:
  4047.  
  4048.      Assertion failed: ptr != 0, file TEST.C, line 16
  4049.      Abnormal program termination
  4050.  
  4051. When a program is running okay, the assert() functions can be removed
  4052. from the compiled program by simply adding the line;
  4053.  
  4054.      #define NDEBUG
  4055.  
  4056. before the #include <assert.h> line. Effectively the assert functions are
  4057. commented out in the preprocessed source before compilation, this means
  4058. that the assert expressions are not evaluated, and thus cannot cause any
  4059. side effects.
  4060.                                     
  4061.                                FLOAT ERRORS
  4062.  
  4063. Floating point numbers are decimal fractions, decimal fractions do not
  4064. accurately equate to normal fractions as not every number will divide
  4065. precisely by ten. This creates the potential for rounding errors in
  4066. calculations that use floating point numbers. The following program
  4067. illustrates one such example of rounding error problems;
  4068.  
  4069.  
  4070.      #include <stdio.h>
  4071.      
  4072.      void main()
  4073.      {
  4074.           float number;
  4075.      
  4076.           for(number = 1; number > 0.4; number -= 0.01)
  4077.                printf("\n%f",number);
  4078.      }
  4079.      
  4080. At about 0.47 (depending upon the host computer and compiler) the program
  4081. starts to store an inaccurate value for 'number'.
  4082.  
  4083. This problem can be minimised by using longer floating point numbers,
  4084. doubles or long doubles that have larger storage space allocated to them.
  4085. For really accurate work though, you should use integers and only convert
  4086. to a floating point number for display. You also should notice that most
  4087. C compilers default floating point numbers to `doubles' and when using
  4088. `float' types have to convert the double down to a float!
  4089.  
  4090.  
  4091.                                     
  4092.                              ERROR HANDLING
  4093.  
  4094. When a system error occurs within a program, for example when an attempt
  4095. to open a file fails, it is helpful to the program's user to display a
  4096. message reporting the failure. Equally it is useful to the program's
  4097. developer to know why the error occurred, or at least as much about it as
  4098. possible. To this end the ANSI standard on C describes a function,
  4099. perror(), which has the prototype;
  4100.  
  4101.  
  4102.      void perror(const char *s);
  4103.  
  4104. and is used to display an error message. The program's own prefixed error
  4105. message is passed to perror() as the string parameter. This error message
  4106. is displayed by perror() followed by the host's system error separated by
  4107. a colon. The following example illustrates a use for perror();
  4108.  
  4109.  
  4110.      #include <stdio.h>
  4111.      
  4112.      void main()
  4113.      {
  4114.           FILE *fp;
  4115.           char fname[] = "none.xyz";
  4116.      
  4117.           fp = fopen(fname,"r");
  4118.      
  4119.           if(!fp)
  4120.                perror(fname);
  4121.           return;
  4122.      }
  4123.      
  4124. If the fopen() operation fails, a message similar to;
  4125.  
  4126.      none.xyz: No such file or directory
  4127.  
  4128. is displayed.
  4129.  
  4130. You should note, perror() sends its output to the predefined stream
  4131. `stderr', which is usually the host computer's display unit.
  4132.  
  4133.  
  4134. perror() finds its message from the host computer via the global variable
  4135. 'errno' that is set by most, but not all system functions.
  4136.  
  4137. Unpleasant errors might justify the use of abort(). abort() is a function
  4138. that terminates the running program with a message;
  4139.  
  4140.      "Abnormal program termination"
  4141.  
  4142. and returns an exit code of 3 to the parent process or operating system.
  4143.  
  4144.  
  4145.  
  4146. Critical Error Handling With The IBM PC AND DOS
  4147.  
  4148. The IBM PC DOS operating system provides a user amendable critical error
  4149. handling function. This function is usually discovered by attempting to
  4150. write to a disk drive that does not have a disk in it, in which case the
  4151. familiar;
  4152.  
  4153.      Not ready error writing drive A
  4154.      Abort Retry Ignore?
  4155.  
  4156. Message is displayed on the screen. Fine when it occurs from within a DOS
  4157. program, not so fine from within your own program!
  4158.  
  4159. The following example program shows how to redirect the DOS critical
  4160. error interrupt to your own function;
  4161.  
  4162.  
  4163.      /* DOS critical error handler test */
  4164.      
  4165.      #include <stdio.h>
  4166.      #include <dos.h>
  4167.      
  4168.      void interrupt new_int();
  4169.      void interrupt (*old_int)();
  4170.      
  4171.      char status;
  4172.      
  4173.      main()
  4174.      {
  4175.           FILE *fp;
  4176.      
  4177.           old_int = getvect(0x24);
  4178.      
  4179.           /* Set critical error handler to my function */
  4180.           setvect(0x24,new_int);
  4181.      
  4182.           /* Generate an error by not having a disc in drive A */
  4183.           fp = fopen("a:\\data.txt","w+");
  4184.      
  4185.           /* Display error status returned */
  4186.           printf("\nStatus ==  %d",status);
  4187.      
  4188.      }
  4189.      
  4190.      void interrupt new_int()
  4191.      {
  4192.           /* set global error code */
  4193.           status = _DI;
  4194.      
  4195.           /* ignore error and return */
  4196.           _AL = 0;
  4197.      }
  4198.      
  4199. When the DOS critical error interrupt is called, a status message is
  4200. passed in the low byte of the DI register. This message is one of;
  4201.  
  4202. Code                     Meaning
  4203.                          
  4204. 00                       Write-protect error
  4205. 01                       Unknown unit
  4206. 02                       Drive not ready
  4207. 03                       Unknown command
  4208. 04                       Data error, bad CRC
  4209. 05                       Bad request structure
  4210.                          length
  4211. 06                       Seek error
  4212. 07                       Unknown media type
  4213. 08                       Sector not found
  4214. 09                       Printer out of paper
  4215. 0A                       Write error
  4216. 0B                       Read error
  4217. 0C                       General failure
  4218.  
  4219. Your critical error interrupt handler can transfer this status message
  4220. into a global variable, and then set the result message held in register
  4221. AL to one of;
  4222.  
  4223.  
  4224. Code                     Action
  4225.                          
  4226. 00                       Ignore error
  4227. 01                       Retry
  4228. 02                       Terminate program
  4229. 03                       Fail (Available with
  4230.                          DOS 3.3 and above)
  4231.  
  4232.  
  4233. If you choose to set AL to 02, terminate program, you should ensure ALL
  4234. files are closed first since DOS will terminate the program abruptly,
  4235. leaving files open and memory allocated, not a pretty state to be in!
  4236.  
  4237.  
  4238. The example program shown returns an ignore status from the critical
  4239. error interrupt, and leaves the checking of any errors to the program
  4240. itself. So, in this example after the call to fopen() we could check the
  4241. return value in fp, and if it reveals an error (NULL in this case) we
  4242. could then check the global variable status and act accordingly, perhaps
  4243. displaying a polite message to the user to put a disk in the floppy drive
  4244. and ensure that the door is closed.
  4245.  
  4246. The following is a practical function for checking whether a specified
  4247. disc drive can be accessed. It should be used with the earlier critical
  4248. error handler and global variable `status'.
  4249.  
  4250.      int DISCOK(int drive)
  4251.      {
  4252.           /* Checks for whether a disc can be read */
  4253.           /* Returns false (zero) on error */
  4254.           /* Thus if(!DISCOK(drive)) */
  4255.           /*          error();  */
  4256.      
  4257.           unsigned char buffer[25];
  4258.      
  4259.           /* Assume okay */
  4260.           status = 0;
  4261.      
  4262.           /* If already logged to disc, return okay */
  4263.           if ('A' + drive == diry[0])
  4264.                return(1);
  4265.      
  4266.           /* Attempt to read disc */
  4267.           memset(buffer,0,20);
  4268.           sprintf(buffer,"%c:$$$.$$$",'A'+drive);
  4269.      
  4270.           _open(buffer,O_RDONLY);
  4271.      
  4272.           /* Check critical error handler status */
  4273.           if (status == 0)
  4274.                return(1);
  4275.      
  4276.           /* Disc cannot be read */
  4277.           return(0);
  4278.      }
  4279.                                     
  4280.                                   CAST
  4281.  
  4282.  
  4283. Casting tells the compiler what a data type is, and can be used to change
  4284. a data type. For example, consider the following;
  4285.  
  4286.      #include <stdio.h>
  4287.      
  4288.      void main()
  4289.      {
  4290.           int x;
  4291.           int y;
  4292.      
  4293.           x = 10;
  4294.           y = 3;
  4295.      
  4296.           printf("\n%lf",x / y);
  4297.      }
  4298.  
  4299. The printf() function has been told to expect a double. However, the
  4300. compiler sees the variables `x' and `y' as integers, and an error occurs!
  4301. To make this example work we must tell the compiler that the result of
  4302. the expression x / y is a double, this is done with a cast thus;
  4303.  
  4304.  
  4305.      #include <stdio.h>
  4306.      
  4307.      void main()
  4308.      {
  4309.           int x;
  4310.           int y;
  4311.      
  4312.           x = 10;
  4313.           y = 3;
  4314.      
  4315.           printf("\n%lf",(double)(x / y));
  4316.      }
  4317.  
  4318. Notice the data type `double' is enclosed by parenthesis, and so is the
  4319. expression to convert. But now, the compiler knows that the result of the
  4320. expression is a double, but it still knows that the variables `x' and `y'
  4321. are integers and so an integer division will be carried out. We have to
  4322. cast the constants thus;
  4323.  
  4324.      #include <stdio.h>
  4325.      
  4326.      void main()
  4327.      {
  4328.           int x;
  4329.           int y;
  4330.      
  4331.           x = 10;
  4332.           y = 3;
  4333.      
  4334.           printf("\n%lf",(double)(x) / (double)(y));
  4335.      }
  4336.  
  4337. Because both of the constants are doubles, the compiler knows that the
  4338. outcome of the expression will also be a double.
  4339.  
  4340.  
  4341.  
  4342.                                     
  4343.                       THE IMPORTANCE OF PROTOTYPING
  4344.  
  4345. Prototyping a function involves letting the compiler know in advance what
  4346. type of values a function will receive and return. For example, lets look
  4347. at strtok(). This has the prototype;
  4348.  
  4349.  
  4350.      char *strtok(char *s1, const char *s2);
  4351.  
  4352. This prototype tells the compiler that strtok() will return a character
  4353. pointer, the first received parameter will be a pointer to a character
  4354. string, and that string can be changed by strtok(), and the last
  4355. parameter will be a pointer to a character string that strtok() cannot
  4356. change.
  4357.  
  4358. The compiler knows how much space to allocate for the return parameter,
  4359. sizeof(char *), but without a prototype for the function the compiler
  4360. will assume that the return value of strtok() is an integer, and will
  4361. allocate space for a return type of int, that is sizeof(int). If an
  4362. integer and a character pointer occupy the same number of bytes on the
  4363. host computer no major problems will occur, but if a character pointer
  4364. occupies more space than an integer, then the compiler wont have
  4365. allocated enough space for the return value and the return from a call to
  4366. strtok() will overwrite some other bit of memory. If, as so often happens
  4367. the return value is returned via the stack, the results of confusing the
  4368. compiler can be disastrous!
  4369.  
  4370. Thankfully most C compilers will warn the programmer if a call to a
  4371. function has been made without a prototype, so that you can add the
  4372. required function prototypes.
  4373.  
  4374. Consider the following example that will not compile on most modern C
  4375. compilers due to the nasty error in it;
  4376.  
  4377.  
  4378.      #include <stdio.h>
  4379.      
  4380.      int FUNCA(int x, int y)
  4381.      {
  4382.           return(MULT(x,y));
  4383.      }
  4384.      
  4385.      double MULT(double x, double y)
  4386.      {
  4387.           return(x * y);
  4388.      }
  4389.      
  4390.      
  4391.      main()
  4392.      {
  4393.           printf("\n%d",FUNCA(5,5));
  4394.      }
  4395.      
  4396. When the compiler first encounters the function MULT() it is as a call
  4397. from within FUNCA(). In the absence of any prototype for MULT() the
  4398. compiler assumes that MULT() returns an integer. When the compiler finds
  4399. the definition for function MULT() it sees that a return of type double
  4400. has been declared. The compiler then reports an error in the compilation
  4401. saying something like;
  4402.  
  4403.  
  4404.      "Type mismatch in redclaration of function 'MULT'"
  4405.  
  4406. What the compiler is really trying to say is, prototype your functions
  4407. before using them! If this example did compile, and was then run it
  4408. probably would crash the computer's stack and cause a system hang.
  4409.  
  4410.                                     
  4411.                           POINTERS TO FUNCTIONS
  4412.  
  4413. C allows a pointer to point to the address of a function, and this
  4414. pointer to be called rather than specifying the function. This is used by
  4415. interrupt changing functions and may be used for indexing functions
  4416. rather than using switch statements. For example;
  4417.  
  4418.      #include <stdio.h>
  4419.      #include <math.h>
  4420.      
  4421.      double (*fp[7])(double x);
  4422.      
  4423.      void main()
  4424.      {
  4425.           double x;
  4426.           int p;
  4427.      
  4428.           fp[0] = sin;
  4429.           fp[1] = cos;
  4430.           fp[2] = acos;
  4431.           fp[3] = asin;
  4432.           fp[4] = tan;
  4433.           fp[5] = atan;
  4434.           fp[6] = ceil;
  4435.      
  4436.           p = 4;
  4437.      
  4438.           x = fp[p](1.5);
  4439.           printf("\nResult %lf",x);
  4440.      }
  4441.  
  4442. This example program defines an array of pointers to functions, (*fp[])()
  4443. that are then called dependant upon the value in the indexing variable p.
  4444. This program could also be written;
  4445.  
  4446.      #include <stdio.h>
  4447.      #include <math.h>
  4448.      
  4449.      void main()
  4450.      {
  4451.           double x;
  4452.           int p;
  4453.      
  4454.           p = 4;
  4455.      
  4456.           switch(p)
  4457.           {
  4458.                case 0 :  x = sin(1.5);
  4459.                      break;
  4460.                case 1 :  x = cos(1.5);
  4461.                      break;
  4462.                case 2 :  x = acos(1.5);
  4463.                      break;
  4464.                case 3 :  x = asin(1.5);
  4465.                      break;
  4466.                case 4 :  x = tan(1.5);
  4467.                      break;
  4468.                case 5 :  x = atan(1.5);
  4469.                      break;
  4470.                case 6 :  x = ceil(1.5);
  4471.                      break;
  4472.           }
  4473.           puts("\nResult %lf",x);
  4474.      }
  4475.  
  4476. The first example, using pointers to the functions, compiles into much
  4477. smaller code and executes faster than the second example.
  4478.  
  4479. The table of pointers to functions is a useful facility when writing
  4480. language interpreters, the program compares an entered instruction
  4481. against a table of key words that results in an index variable being set
  4482. and then the program simply needs to call the function pointer indexed by
  4483. the variable, rather than wading through a lengthy switch() statement.
  4484.  
  4485.                                     
  4486.                             DANGEROUS PITFALLS
  4487.  
  4488. One of the most dangerous pitfalls can occur with the use of gets(). This
  4489. function accepts input from the stream stdin until it receives a newline
  4490. character, which it does not pass to the program. All the data it
  4491. receives is stored in memory starting at the address of the specified
  4492. string, and quite happily overflowing into other variables! This danger
  4493. can be avoided by using fgets() that allows a maximum number of
  4494. characters to be specified, so you can avoid overflow problems. Notice
  4495. though that fgets() does retain the newline character scanf() is another
  4496. function best avoided. It accepts input from stdin and stores the
  4497. received data at the addresses provided to it. If those addresses are not
  4498. really addresses where the data ends up is anybodys guess!
  4499.  
  4500. This example is okay, since scanf() has been told to store the data at
  4501. the addresses occupied by the two variables `x' and `y'.
  4502.  
  4503.  
  4504.      void main()
  4505.      {
  4506.           int x;
  4507.           int y;
  4508.      
  4509.           scanf("%d%d",&x,&y);
  4510.      }
  4511.      
  4512. But in this example scanf() has been told to store the data at the
  4513. addresses suggested by the current values of `x' and `y'! An easy and
  4514. common mistake to make, and yet one that can have very peculiar effects.
  4515.  
  4516.  
  4517.      void main()
  4518.      {
  4519.           int x;
  4520.           int y;
  4521.      
  4522.           scanf("%d%d",x,y);
  4523.      }
  4524.      
  4525. The answer is, don't use scanf(), use fgets() and parse your string
  4526. manually using the standard library functions strtod(), strtol() and
  4527. strtoul().
  4528.  
  4529. Here is the basis of a simple input string parser that returns the
  4530. individual input fields from an entered string;
  4531.  
  4532.      #include <stdio.h>
  4533.      #include <string.h>
  4534.      
  4535.      void main()
  4536.      {
  4537.           char input[80];
  4538.           char *p;
  4539.      
  4540.           puts("\nEnter a string ");
  4541.           fgets(input,79,stdin);
  4542.      
  4543.           /* now parse string for input fields */
  4544.           puts("The fields entered are:");
  4545.           p = strtok(input,", ");
  4546.           while(p)
  4547.           {
  4548.                puts(p);
  4549.                p = strtok(NULL,", ");
  4550.           }
  4551.      }
  4552.      
  4553.                                     
  4554.                                  SIZEOF
  4555.  
  4556. A preprocessor instruction, `sizeof', returns the size of an item, be it
  4557. a structure, pointer, string or whatever. However! take care when using
  4558. `sizeof'. Consider the following program;
  4559.  
  4560.  
  4561.      #include <stdio.h>
  4562.      #include <mem.h>
  4563.      
  4564.      char string1[80]; char *text = "This is a string of data" ;
  4565.      
  4566.      void main()
  4567.      {
  4568.           /* Initialise string1 correctly */
  4569.           memset(string1,0,sizeof(string1));
  4570.      
  4571.           /* Copy some text into string1 ? */
  4572.           memcpy(string1,text,sizeof(text));
  4573.      
  4574.           /* Display string1 */
  4575.           printf("\nString 1 = %s\n",string1);
  4576.      }
  4577.      
  4578. What it is meant to do is initialise all 80 elements of string1 to
  4579. zeroes, which it does alright, and then copy the constant string `text'
  4580. into the variable `string1'. However, variable text is a pointer, and so
  4581. the sizeof(text) instruction returns the size of the character pointer
  4582. (perhaps two bytes) rather than the length of the string pointed to by
  4583. the pointer!  If the length of the string pointed to by `text' happened
  4584. to be the same as the size of a character pointer then no error would be
  4585. noticed.
  4586.  
  4587.                                     
  4588.                                INTERRUPTS
  4589.  
  4590. The IBM PC BIOS and DOS contain functions that may be called by a program
  4591. by way of the function's interrupt number. The address of the function
  4592. assigned to each interrupt is recorded in a table in RAM called the
  4593. interrupt vector table. By changing the address of an interrupt vector a
  4594. program can effectively disable the original interrupt function and
  4595. divert any calls to it to its own function. This was done by the critical
  4596. error handler described in the section on error handling.
  4597.  
  4598. Borland's Turbo C provides two library functions for reading and changing
  4599. an interrupt vector. These are: setvect() and getvect(). The
  4600. corresponding Microsoft C library functions are: _dos_getvect() and
  4601. _dos_setvect().
  4602.  
  4603. getvect() has the function prototype;
  4604.  
  4605.      void interrupt(*getvect(int interrupt_no))();
  4606.  
  4607. setvect() has the prototype;
  4608.  
  4609.      void setvect(int interrupt_no, void interrupt(*func)());
  4610.  
  4611. To read and save the address of an existing interrupt a program uses
  4612. getvect() thus;
  4613.  
  4614.      /* Declare variable to record old interrupt */
  4615.      void interrupt(*old)(void);
  4616.      
  4617.      main()
  4618.      {
  4619.           /* get old interrupt vector */
  4620.           old = getvect(0x1C);
  4621.           .
  4622.           .
  4623.           .
  4624.      }
  4625.      
  4626. Where 0x1C is the interrupt vector to be retrieved.
  4627.  
  4628. To then set the interrupt vector to a new address, our own function, we
  4629. use setvect() thus;
  4630.  
  4631.      void interrupt new(void)
  4632.      {
  4633.           .
  4634.           .
  4635.           /* New interrupt function */
  4636.           .
  4637.           .
  4638.           .
  4639.      }
  4640.      
  4641.      main()
  4642.      {
  4643.           .
  4644.           .
  4645.           .
  4646.           setvect(0x1C,new);
  4647.           .
  4648.           .
  4649.           .
  4650.           .
  4651.      }
  4652.      
  4653. There are two important points to note about interrupts;
  4654.  
  4655. First, if the interrupt is called by external events then before changing
  4656. the vector you MUST disable the interrupt callers using disable() and
  4657. then re-enable the interrupts after the vector has been changed using
  4658. enable().  If a call is made to the interrupt while the vector is being
  4659. changed ANYTHING could happen!
  4660.  
  4661. Secondly, before your program terminates and returns to DOS you must
  4662. reset any changed interrupt vectors! The exception to this is the
  4663. critical error handler interrupt vector that is restored automatically by
  4664. DOS, so your program needn't bother restoring it.
  4665.  
  4666. This example program hooks the PC clock timer interrupt to provide a
  4667. background clock process while the rest of the program continues to run.
  4668. If included with your own program that requires a constantly displayed
  4669. clock on screen, you need only amend the display coordinates in the call
  4670. to puttext(). Sincle the closk display code is called by a hardware
  4671. issued interrupt, your program can start the clock and forget it until it
  4672. terminates.
  4673.  
  4674.      
  4675.      /* Compile in LARGE memory model */
  4676.      
  4677.      #include <stdio.h>
  4678.      #include <dos.h>
  4679.      #include <time.h>
  4680.      #include <conio.h>
  4681.      #include <stdlib.h>
  4682.      
  4683.      enum { FALSE, TRUE };
  4684.      
  4685.      #define COLOUR (BLUE << 4) | YELLOW
  4686.      
  4687.      #define BIOS_TIMER  0x1C
  4688.      
  4689.      static unsigned installed = FALSE;
  4690.      static void interrupt (*old_tick) (void);
  4691.      
  4692.      static void interrupt tick (void)
  4693.      {
  4694.           int i;
  4695.           struct tm *now;
  4696.           time_t this_time;
  4697.           char time_buf[9];
  4698.           static time_t last_time = 0L;
  4699.           static char video_buf[20] =
  4700.           {
  4701.                ' ', COLOUR, '0', COLOUR, '0', COLOUR, ':', COLOUR, '0',
  4702.      COLOUR,
  4703.                '0', COLOUR, ':', COLOUR, '0', COLOUR, '0', COLOUR, ' ',
  4704.      COLOUR
  4705.           };
  4706.      
  4707.           enable ();
  4708.      
  4709.           if (time (&this_time) != last_time)
  4710.           {
  4711.                last_time = this_time;
  4712.      
  4713.                now = localtime(&this_time);
  4714.      
  4715.                sprintf(time_buf, "%02d:%02d.%02d",now->tm_hour,now-
  4716.      >tm_min,now->tm_sec);
  4717.      
  4718.                for (i = 0; i < 8; i++)
  4719.                {
  4720.                     video_buf[(i + 1) << 1] = time_buf[i];
  4721.                }
  4722.      
  4723.                puttext (71, 1, 80, 1, video_buf);
  4724.           }
  4725.      
  4726.           old_tick ();
  4727.      }
  4728.      
  4729.      void stop_clock (void)
  4730.      {
  4731.           if (installed)
  4732.           {
  4733.                setvect (BIOS_TIMER, old_tick);
  4734.                installed = FALSE;
  4735.           }
  4736.      }
  4737.      
  4738.      void start_clock (void)
  4739.      {
  4740.           static unsigned first_time = TRUE;
  4741.      
  4742.           if (!installed)
  4743.           {
  4744.                if (first_time)
  4745.                {
  4746.                     atexit (stop_clock);
  4747.                     first_time = FALSE;
  4748.                }
  4749.      
  4750.                old_tick = getvect (BIOS_TIMER);
  4751.                setvect (BIOS_TIMER, tick);
  4752.                installed = TRUE;
  4753.           }
  4754.      }
  4755.                                     
  4756.                                  SIGNAL
  4757.  
  4758. Interrupts raised by the host computer can be trapped and diverted in
  4759. several ways. A simple method is to use signal().
  4760.  
  4761. Signal() takes two parameters in the form;
  4762.  
  4763.      void (*signal (int sig, void (*func) (int))) (int);
  4764.  
  4765. The first parameter, `sig' is the signal to be caught. These are often
  4766. predefined by the header file `signal.h'.
  4767.  
  4768. The second parameter is a pointer to a function to be called when the
  4769. signal is raised. This can either be a user function, or a macro defined
  4770. in the header file `signal.h' to do some arbitrary task, such as ignore
  4771. the signal for example.
  4772.  
  4773. On a PC platform, it is often useful to disable the `ctrl-break' key
  4774. combination that is used to terminate a running program by the user. The
  4775. following PC signal() call replaces the predefined signal `SIGINT', which
  4776. equates to the ctrl-break interrupt request, with the predefined macro
  4777. `SIG-IGN', ignore the request;
  4778.  
  4779.  
  4780.      signal(SIGINT,SIG_IGN);
  4781.  
  4782. This example catches floating point errors on a PC, and zero divisions!
  4783.  
  4784.      #include <stdio.h>
  4785.      #include <signal.h>
  4786.      
  4787.      void (*old_sig)();
  4788.      
  4789.      void catch(int sig)
  4790.      {
  4791.           printf("Catch was called with: %d\n",sig);
  4792.      }
  4793.      
  4794.      
  4795.      void main()
  4796.      {
  4797.           int a;
  4798.           int b;
  4799.      
  4800.           old_sig = signal(SIGFPE,catch);
  4801.      
  4802.           a = 0;
  4803.           b = 10 / a;
  4804.      
  4805.           /* Restore original handler before exiting! */
  4806.           signal(SIGFPE,old_sig);
  4807.      }
  4808.      
  4809.                                     
  4810.                           SORTING AND SEARCHING
  4811.  
  4812. The ANSI C standard defines qsort(), a function for sorting a table of
  4813. data. The function follows the format;
  4814.  
  4815.      qsort(void *base,size_t elements,size_t width,int (*cmp)(void *,
  4816. void *));
  4817.  
  4818. The following short program illustrates the use of qsort() with a
  4819. character array.
  4820.  
  4821.      #include <string.h>
  4822.      
  4823.      main()
  4824.      {
  4825.           int n;
  4826.           char data[10][20];
  4827.      
  4828.           /* Initialise some arbirary data */
  4829.      
  4830.           strcpy(data[0],"RED");
  4831.           strcpy(data[1],"BLUE");
  4832.           strcpy(data[2],"GREEN");
  4833.           strcpy(data[3],"YELLOW");
  4834.           strcpy(data[4],"INDIGO");
  4835.           strcpy(data[5],"BROWN");
  4836.           strcpy(data[6],"BLACK");
  4837.           strcpy(data[7],"ORANGE");
  4838.           strcpy(data[8],"PINK");
  4839.           strcpy(data[9],"CYAN");
  4840.      
  4841.           /* Sort the data table */
  4842.           qsort(data[0],10,20,strcmp);
  4843.      
  4844.           /* Print the data table */
  4845.           for(n = 0; n < 10; n++)
  4846.                puts(data[n]);
  4847.      }
  4848.      
  4849.  
  4850. Here is a program that implements the shell sort algorithm (this one is
  4851. based on the routine in K & R), which sorts arrays of pointers based upon
  4852. the data pointed to by the pointers;
  4853.  
  4854.      #include <stdio.h>
  4855.      #include <stdlib.h>
  4856.      #include <string.h>
  4857.      
  4858.      #define LINELEN     80
  4859.      #define MAXLINES    2000
  4860.      
  4861.      char *lines[MAXLINES];
  4862.      int lastone;
  4863.      
  4864.      void SHELL(void);
  4865.      
  4866.      void SHELL()
  4867.      {
  4868.           /* SHELL Sort Courtesy of K & R */
  4869.      
  4870.           int gap;
  4871.           int i;
  4872.           int j;
  4873.           char temp[LINELEN];
  4874.      
  4875.           for(gap = lastone / 2; gap > 0; gap /= 2)
  4876.           for(i = gap; i < lastone; i++)
  4877.                for(j = i - gap; j >= 0 && strcmp(lines[j] , lines[j +
  4878.      gap]) >
  4879.                   0; j -= gap)
  4880.                {
  4881.                     strcpy(temp,lines[j]);
  4882.                     strcpy(lines[j] , lines[j + gap]);
  4883.                     strcpy(lines[j + gap] , temp);
  4884.      
  4885.                }
  4886.      }
  4887.      
  4888.      void main(int argc, char *argv[])
  4889.      {
  4890.           FILE *fp;
  4891.           char buff[100];
  4892.           int n;
  4893.  
  4894.           /* Check command line parameter has been given */
  4895.           if (argc != 2)
  4896.           {
  4897.                printf("\nError: Usage is SERVSORT file");
  4898.                exit(0);
  4899.           }
  4900.      
  4901.           /* Attempt to open file for updating */
  4902.           fp = fopen(argv[1],"r+");
  4903.           if (fp == NULL)
  4904.           {
  4905.                printf("\nError: Unable to open %s",argv[1]);
  4906.                exit(0);
  4907.           }
  4908.      
  4909.           /* Initialise element counter to zero */
  4910.           lastone = 0;
  4911.      
  4912.           /* Read file to be sorted */
  4913.           while((fgets(buff,100,fp)) != NULL)
  4914.           {
  4915.                /* Allocate memory block*/
  4916.                lines[lastone] = malloc(LINELEN);
  4917.                if (lines[lastone] == NULL)
  4918.                {
  4919.                     printf("\nError: Unable to allocate memory");
  4920.                     fclose(fp);
  4921.                     exit(0);
  4922.                }
  4923.                strcpy(lines[lastone],buff);
  4924.                lastone++;
  4925.      
  4926.                if (lastone > MAXLINES)
  4927.                {
  4928.                     printf("\nError: Too many lines in source file");
  4929.                     exit(0);
  4930.                }
  4931.           }
  4932.           /* Call sort function */
  4933.           SHELL();
  4934.      
  4935.           /* Close file */
  4936.           fclose(fp);
  4937.      
  4938.           /* Reopen file in create mode */
  4939.           fp = fopen(argv[1],"w+");
  4940.      
  4941.           /* Copy sorted data from memory to disk */
  4942.           for(n = 0; n < lastone; n++)
  4943.                fputs(lines[n],fp);
  4944.      
  4945.           /* Close file finally */
  4946.           fclose(fp);
  4947.      
  4948.           /* Return to calling program */
  4949.           return(1);
  4950.      }
  4951.      
  4952.  
  4953. If we want to use qsort() with a table of pointers we have to be a bit
  4954. more clever than usual.
  4955.  
  4956. This example uses the colours again, but this time they are stored in
  4957. main memory and indexed by a table of pointers. Because we have a table
  4958. of pointers to sort there are two differences between this program's
  4959. qsort() and the previous one;
  4960.  
  4961. First we can't use strcmp() as the qsort() comparison function, secondly
  4962. the width of the table being sorted is sizeof(char *), that is the size
  4963. of a character pointer.
  4964.  
  4965. Notice the comparison function cmp() that receives two parameters, both
  4966. are pointers to a pointer. qsort() sends to this function the values held
  4967. in data[], which are in turn pointers to the data. So we need to use this
  4968. indirection to locate the data, otherwise we would be comparing the
  4969. addresses at which the data is held rather than the data itself!
  4970.  
  4971.      #include <alloc.h>
  4972.      #include <string.h>
  4973.      
  4974.      /* Function prototype for comparison function */
  4975.      int (cmp)(char **,char **);
  4976.      
  4977.      int cmp(char **s1, char **s2)
  4978.      {
  4979.           /* comparison function using pointers to pointers */
  4980.           return(strcmp(*s1,*s2));
  4981.      }
  4982.      
  4983.      main()
  4984.      {
  4985.           int n;
  4986.           char *data[10];
  4987.      
  4988.           for(n = 0; n < 10; n++)
  4989.                data[n] = malloc(20);
  4990.      
  4991.           strcpy(data[0],"RED");
  4992.           strcpy(data[1],"BLUE");
  4993.           strcpy(data[2],"GREEN");
  4994.           strcpy(data[3],"YELLOW");
  4995.           strcpy(data[4],"INDIGO");
  4996.           strcpy(data[5],"BROWN");
  4997.           strcpy(data[6],"BLACK");
  4998.           strcpy(data[7],"ORANGE");
  4999.           strcpy(data[8],"PINK");
  5000.           strcpy(data[9],"CYAN");
  5001.      
  5002.           /* The data table is comprised of pointers */
  5003.           /* so the call to qsort() must reflect this */
  5004.           qsort(data,10,sizeof(char *),cmp);
  5005.      
  5006.           for(n = 0; n < 10; n++)
  5007.                puts(data[n]);
  5008.      }
  5009.      
  5010. The quick sort is a fast sorting algorithm that works by subdividing the
  5011. data table into two sub-tables and then subdividing the sub-tables. As it
  5012. subdivides the table, so it compares the elements in the table and swaps
  5013. them as required.
  5014.  
  5015. The following program implements the quick sort algorithm, which is
  5016. usually already used by qsort();
  5017.  
  5018.  
  5019.      #include <string.h>
  5020.      
  5021.      #define MAXELE  2000
  5022.      
  5023.      char data[10][20];
  5024.      int lastone;
  5025.      
  5026.      void QKSORT()
  5027.      {
  5028.           /* Implementation of QUICKSORT algorithm */
  5029.      
  5030.           int i;
  5031.           int j;
  5032.           int l;
  5033.           int p;
  5034.           int r;
  5035.           int s;
  5036.           char temp[100];
  5037.           static int sl[MAXELE][2];
  5038.      
  5039.           /* sl[] is an index to the sub-table */
  5040.      
  5041.           l = 0;
  5042.           r = lastone;
  5043.           p = 0;
  5044.      
  5045.           do
  5046.           {
  5047.                while(l < r)
  5048.                {
  5049.                     i = l;
  5050.                     j = r;
  5051.                     s = -1;
  5052.      
  5053.                     while(i < j)
  5054.                     {
  5055.                          if (strcmp(data[i],data[j]) > 0)
  5056.                          {
  5057.                               strcpy(temp,data[i]);
  5058.                               strcpy(data[i],data[j]);
  5059.                               strcpy(data[j],temp);
  5060.                               s = 0 - s;
  5061.                          }
  5062.      
  5063.                          if (s == 1)
  5064.                               i++;
  5065.                          else
  5066.                               j--;
  5067.                     }
  5068.      
  5069.                     if (i + 1 < r)
  5070.                     {
  5071.                          p++;
  5072.                          sl[p][0] = i + 1;
  5073.                          sl[p][1] = r;
  5074.                     }
  5075.                     r = i - 1;
  5076.                }
  5077.                if (p != 0)
  5078.                {
  5079.                     l = sl[p][0];
  5080.                     r = sl[p][1];
  5081.                     p--;
  5082.                }
  5083.           }
  5084.           while(p > 0);
  5085.      }
  5086.      
  5087.      main()
  5088.      {
  5089.           int n;
  5090.      
  5091.           /* Initialise arbitrary data */
  5092.           strcpy(data[0],"RED");
  5093.           strcpy(data[1],"BLUE");
  5094.           strcpy(data[2],"GREEN");
  5095.           strcpy(data[3],"YELLOW");
  5096.           strcpy(data[4],"INDIGO");
  5097.           strcpy(data[5],"BROWN");
  5098.           strcpy(data[6],"BLACK");
  5099.           strcpy(data[7],"ORANGE");
  5100.           strcpy(data[8],"PINK");
  5101.           strcpy(data[9],"CYAN");
  5102.      
  5103.           /* Set last element indicator */
  5104.           lastone = 9;
  5105.      
  5106.           /* Call quick sort function */
  5107.           QKSORT();
  5108.      
  5109.           /* Display sorted list */
  5110.           for(n = 0; n < 10; n++)
  5111.                puts(data[n]);
  5112.      
  5113.      }
  5114.      
  5115. A table sorted into ascending order can be searched with bsearch(), this
  5116. takes the format;
  5117.  
  5118.      bsearch(key,base,num_elements,width,int (*cmp)(void *, void *));
  5119.  
  5120. bsearch() returns a pointer to the first element in the table that
  5121. matches the key, or zero if no match is found.
  5122.  
  5123. Or you can write your own binary search function thus;
  5124.  
  5125.      int BSRCH(char *key, void *data, int numele, int width)
  5126.      {
  5127.           /* A binary search function returning one if found */
  5128.           /* Zero if not found */
  5129.      
  5130.           int bp;
  5131.           int tp;
  5132.           int mp;
  5133.           int result;
  5134.           char *p;
  5135.      
  5136.           bp = 0;
  5137.           tp = numele;
  5138.           mp = (tp + bp) / 2;
  5139.      
  5140.           /* Locate element mp in table by assigning pointer to start */
  5141.           /* and incrementing it by width * mp */
  5142.           p = data;
  5143.           p += width * mp;
  5144.      
  5145.           while((result = strcmp(p,key)) != 0)
  5146.           {
  5147.                if (mp >= tp)
  5148.                     /* Not found! */
  5149.                     return(0);
  5150.                if (result < 0)
  5151.                     bp = mp + 1;
  5152.                else
  5153.                     tp = mp - 1;
  5154.      
  5155.                mp = (bp + tp) / 2;
  5156.                p = data;
  5157.                p += width * mp;
  5158.           }
  5159.           return(1);
  5160.      }
  5161.      
  5162.      void main()
  5163.      {
  5164.           int result;
  5165.           char data[10][20];
  5166.      
  5167.           /* Initialise some arbirary data */
  5168.      
  5169.           strcpy(data[0],"RED");
  5170.           strcpy(data[1],"BLUE");
  5171.           strcpy(data[2],"GREEN");
  5172.           strcpy(data[3],"YELLOW");
  5173.           strcpy(data[4],"INDIGO");
  5174.           strcpy(data[5],"BROWN");
  5175.           strcpy(data[6],"BLACK");
  5176.           strcpy(data[7],"ORANGE");
  5177.           strcpy(data[8],"PINK");
  5178.           strcpy(data[9],"CYAN");
  5179.      
  5180.           /* Sort the data table */
  5181.           qsort(data[0],10,20,strcmp);
  5182.      
  5183.           result = BSRCH("CYAN",data[0],10,20);
  5184.      
  5185.           printf("\n%s\n",(result == 0) ? "Not found" : "Located okay");
  5186.      }
  5187.      
  5188. There are other sorting algorithms as well. This program incorporates the
  5189. QUICK SORT, BUBBLE SORT, FAST BUBBLE SORT, INSERTION SORT and SHELL SORT
  5190. for comparing how each performs on a random 1000 item string list;
  5191.  
  5192.      #include <stdio.h>
  5193.      #include <stdlib.h>
  5194.      #include <string.h>
  5195.      
  5196.      char data[1000][4];
  5197.      char save[1000][4];
  5198.      
  5199.      int lastone;
  5200.      
  5201.      void INITDATA(void);
  5202.      void QKSORT(void);
  5203.      void SHELL(void);
  5204.      void BUBBLE(void);
  5205.      void FBUBBLE(void);
  5206.      void INSERTION(void);
  5207.      void MKDATA(void);
  5208.      
  5209.      void QKSORT()
  5210.      {
  5211.           /* Implementation of QUICKSORT algorithm */
  5212.      
  5213.           int i;
  5214.           int j;
  5215.           int l;
  5216.           int p;
  5217.           int r;
  5218.           int s;
  5219.           char temp[20];
  5220.           static int sl[1000][2];
  5221.      
  5222.           l = 0;
  5223.           r = lastone;
  5224.           p = 0;
  5225.      
  5226.           do
  5227.           {
  5228.                while(l < r)
  5229.                {
  5230.                     i = l;
  5231.                     j = r;
  5232.                     s = -1;
  5233.      
  5234.                     while(i < j)
  5235.                     {
  5236.                          if (strcmp(data[i],data[j]) > 0)
  5237.                          {
  5238.                               strcpy(temp,data[i]);
  5239.                               strcpy(data[i],data[j]);
  5240.                               strcpy(data[j],temp);
  5241.                               s = 0 - s;
  5242.                          }
  5243.      
  5244.                          if (s == 1)
  5245.                               i++;
  5246.                          else
  5247.                               j--;
  5248.                     }
  5249.      
  5250.                     if (i + 1 < r)
  5251.                     {
  5252.                          p++;
  5253.                          sl[p][0] = i + 1;
  5254.                          sl[p][1] = r;
  5255.                     }
  5256.                     r = i - 1;
  5257.                }
  5258.                if (p != 0)
  5259.                {
  5260.                     l = sl[p][0];
  5261.                     r = sl[p][1];
  5262.                     p--;
  5263.                }
  5264.           }
  5265.           while(p > 0);
  5266.      }
  5267.      
  5268.      void SHELL()
  5269.      {
  5270.           /* SHELL Sort Courtesy of K & R */
  5271.      
  5272.           int gap;
  5273.           int i;
  5274.           int j;
  5275.           char temp[20];
  5276.      
  5277.           for(gap = lastone / 2; gap > 0; gap /= 2)
  5278.           for(i = gap; i < lastone; i++)
  5279.                for(j = i - gap; j >= 0 && strcmp(data[j] , data[j + gap])
  5280.      > 0;
  5281.                       j -= gap)
  5282.                {
  5283.                     strcpy(temp,data[j]);
  5284.                     strcpy(data[j] , data[j + gap]);
  5285.                     strcpy(data[j + gap] , temp);
  5286.                }
  5287.      }
  5288.      
  5289.      void BUBBLE()
  5290.      {
  5291.           int a;
  5292.           int b;
  5293.           char temp[20];
  5294.      
  5295.           for(a = lastone; a >= 0; a--)
  5296.           {
  5297.                for(b = 0; b < a; b++)
  5298.                {
  5299.                     if(strcmp(data[b],data[b + 1]) > 0)
  5300.                     {
  5301.                          strcpy(temp,data[b]);
  5302.                          strcpy(data[b] , data[b + 1]);
  5303.                          strcpy(data[b + 1] , temp);
  5304.                     }
  5305.                }
  5306.           }
  5307.      }
  5308.      
  5309.      void FBUBBLE()
  5310.      {
  5311.           /* bubble sort with swap flag*/
  5312.      
  5313.           int a;
  5314.           int b;
  5315.           int s;
  5316.           char temp[20];
  5317.      
  5318.           s = 1;
  5319.      
  5320.           for(a = lastone; a >= 0 && s == 1; a--)
  5321.           {
  5322.                s = 0;
  5323.                for(b = 0; b < a; b++)
  5324.                {
  5325.                     if(strcmp(data[b],data[b + 1]) > 0)
  5326.                     {
  5327.                          strcpy(temp,data[b]);
  5328.                          strcpy(data[b] , data[b + 1]);
  5329.                          strcpy(data[b + 1] , temp);
  5330.                          s = 1;
  5331.                     }
  5332.                }
  5333.           }
  5334.      }
  5335.      
  5336.      void INSERTION()
  5337.      {
  5338.           int a;
  5339.           int b;
  5340.           char temp[20];
  5341.      
  5342.           for(a = 0; a < lastone; a++)
  5343.           {
  5344.                b = a;
  5345.                strcpy(temp,data[a + 1]);
  5346.                while(b >= 0)
  5347.                {
  5348.                     if (strcmp(temp,data[b]) < 0)
  5349.                     {
  5350.                          strcpy(data[b+1],data[b]);
  5351.                          b--;
  5352.                     }
  5353.                     else
  5354.                          break;
  5355.                }
  5356.                strcpy(data[b+1],temp);
  5357.           }
  5358.      }
  5359.      
  5360.      void MKDATA()
  5361.      {
  5362.           /* Initialise arbitrary data */
  5363.           /* Uses random(), which is not ANSI C */
  5364.           /* Returns a random number between 0 and n - 1 */
  5365.      
  5366.           int n;
  5367.           for(n = 0; n < 1000; n++)
  5368.                sprintf(save[n],"%d",random(1000));
  5369.      }
  5370.      
  5371.      void INITDATA()
  5372.      {
  5373.           int n;
  5374.      
  5375.           for(n = 0 ; n < 1000; n++)
  5376.                strcpy(data[n],save[n]);
  5377.      }
  5378.      
  5379.      void main()
  5380.      {
  5381.           MKDATA();
  5382.      
  5383.           /* Initialise arbitrary data */
  5384.           INITDATA();
  5385.      
  5386.           /* Set last element indicator */
  5387.           lastone = 999;
  5388.      
  5389.           /* Call quick sort function */
  5390.           QKSORT();
  5391.      
  5392.      
  5393.           /* Initialise arbitrary data */
  5394.           INITDATA();
  5395.      
  5396.           /* Set last element indicator */
  5397.           lastone = 1000;
  5398.      
  5399.           /* Call shell sort function */
  5400.           SHELL();
  5401.      
  5402.           /* Initialise arbitrary data */
  5403.           INITDATA();
  5404.      
  5405.           /* Set last element indicator */
  5406.           lastone = 999;
  5407.      
  5408.           /* Call bubble sort function */
  5409.           BUBBLE();
  5410.      
  5411.           /* Initialise arbitrary data */
  5412.           INITDATA();
  5413.      
  5414.           /* Set last element indicator */
  5415.           lastone = 999;
  5416.      
  5417.           /* Call bubble sort with swap flag function */
  5418.           FBUBBLE();
  5419.      
  5420.           /* Initialise arbitrary data */
  5421.           INITDATA();
  5422.      
  5423.           /* Set last element indicator */
  5424.           lastone = 999;
  5425.      
  5426.           /* Call insertion sort function */
  5427.           INSERTION();
  5428.      }
  5429.  
  5430. Here are the profiler results of the above test program run on 1000 and
  5431. 5000 random items;
  5432.  
  5433. STRING SORT - 1000 RANDOM ITEMS
  5434.  
  5435. FBUBBLE        26.436 sec  41%
  5436. |********************************************
  5437. BUBBLE         26.315 sec  41%
  5438. |*******************************************
  5439. INSERTION      10.210 sec  15% |***************
  5440. SHELL               0.8050 sec   1%  |*
  5441. QKSORT         0.3252 sec  <1% |
  5442.  
  5443. STRING SORT - 5000 RANDOM ITEMS
  5444.  
  5445. FBUBBLE        563.70 sec  41%
  5446. |********************************************
  5447. BUBBLE         558.01 sec  41%
  5448. |********************************************
  5449. INSERTION      220.61 sec  16% |***************
  5450. SHELL               5.2531 sec  <1% |
  5451. QKSORT         0.8379 sec  <1% |
  5452.  
  5453. Here is the same test program amended for sorting tables of integers;
  5454.  
  5455.      /* Integer sort test program */
  5456.      
  5457.      #include <stdio.h>
  5458.      #include <stdlib.h>
  5459.      
  5460.      void INITDATA(void);
  5461.      void QKSORT(void);
  5462.      void SHELL(void);
  5463.      void BUBBLE(void);
  5464.      void FBUBBLE(void);
  5465.      void INSERTION(void);
  5466.      void MKDATA(void);
  5467.      
  5468.      int data[1000];
  5469.      int save[1000];
  5470.      
  5471.      int lastone;
  5472.      
  5473.      void QKSORT()
  5474.      {
  5475.           /* Implementation of QUICKSORT algorithm */
  5476.      
  5477.           int i;
  5478.           int j;
  5479.           int l;
  5480.           int p;
  5481.           int r;
  5482.           int s;
  5483.           int temp;
  5484.           static int sl[1000][2];
  5485.      
  5486.           l = 0;
  5487.           r = lastone;
  5488.           p = 0;
  5489.      
  5490.           do
  5491.           {
  5492.                while(l < r)
  5493.                {
  5494.                     i = l;
  5495.                     j = r;
  5496.                     s = -1;
  5497.      
  5498.                     while(i < j)
  5499.                     {
  5500.                          if (data[i] > data[j])
  5501.                          {
  5502.                               temp = data[i];
  5503.                               data[i] = data[j];
  5504.                               data[j] = temp;
  5505.                               s = 0 - s;
  5506.                          }
  5507.      
  5508.                          if (s == 1)
  5509.                               i++;
  5510.                          else
  5511.                               j--;
  5512.                     }
  5513.      
  5514.                     if (i + 1 < r)
  5515.                     {
  5516.                          p++;
  5517.                          sl[p][0] = i + 1;
  5518.                          sl[p][1] = r;
  5519.                     }
  5520.                     r = i - 1;
  5521.                }
  5522.                if (p != 0)
  5523.                {
  5524.                     l = sl[p][0];
  5525.                     r = sl[p][1];
  5526.                     p--;
  5527.                }
  5528.           }
  5529.           while(p > 0);
  5530.      }
  5531.      
  5532.      void SHELL()
  5533.      {
  5534.           /* SHELL Sort Courtesy of K & R */
  5535.      
  5536.           int gap;
  5537.           int i;
  5538.           int j;
  5539.           int temp;
  5540.      
  5541.           for(gap = lastone / 2; gap > 0; gap /= 2)
  5542.           for(i = gap; i < lastone; i++)
  5543.                for(j = i - gap; j >= 0 && data[j] > data[j + gap];
  5544.                       j -= gap)
  5545.                {
  5546.                     temp = data[j];
  5547.                     data[j] = data[j + gap];
  5548.                     data[j + gap] = temp;
  5549.                }
  5550.      }
  5551.      
  5552.      void BUBBLE()
  5553.      {
  5554.           int a;
  5555.           int b;
  5556.           int temp;
  5557.      
  5558.           for(a = lastone; a >= 0; a--)
  5559.           {
  5560.                for(b = 0; b < a; b++)
  5561.                {
  5562.                     if(data[b] > data[b + 1])
  5563.                     {
  5564.                          temp = data[b];
  5565.                          data[b] = data[b + 1];
  5566.                          data[b + 1] = temp;
  5567.                     }
  5568.                }
  5569.           }
  5570.      }
  5571.      
  5572.      void FBUBBLE()
  5573.      {
  5574.           /* bubble sort with swap flag */
  5575.      
  5576.           int a;
  5577.           int b;
  5578.           int s;
  5579.           int temp;
  5580.      
  5581.           s = 1;
  5582.      
  5583.           for(a = lastone; a >= 0 && s == 1; a--)
  5584.           {
  5585.                s = 0;
  5586.                for(b = 0; b < lastone - a; b++)
  5587.                {
  5588.                     if(data[b] > data[b + 1])
  5589.                     {
  5590.                          temp = data[b];
  5591.                          data[b] = data[b + 1];
  5592.                          data[b + 1] = temp;
  5593.                          s = 1;
  5594.                     }
  5595.                }
  5596.           }
  5597.      }
  5598.      
  5599.      void INSERTION()
  5600.      {
  5601.           int a;
  5602.           int b;
  5603.           int temp;
  5604.      
  5605.           for(a = 0; a < lastone; a++)
  5606.           {
  5607.                b = a;
  5608.                temp = data[a + 1];
  5609.                while(b >= 0)
  5610.                {
  5611.                     if (temp < data[b])
  5612.                     {
  5613.                          data[b+1] = data[b];
  5614.                          b--;
  5615.                     }
  5616.                     else
  5617.                          break;
  5618.                }
  5619.                data[b+1] = temp;
  5620.           }
  5621.      }
  5622.      
  5623.      void MKDATA()
  5624.      {
  5625.           int n;
  5626.      
  5627.           for(n = 0; n < 1000; n++)
  5628.                save[n] = random(1000);
  5629.      }
  5630.      
  5631.      void INITDATA()
  5632.      {
  5633.           int n;
  5634.      
  5635.           for(n = 0; n < 1000; n++)
  5636.           data[n] = save[n];
  5637.      }
  5638.      
  5639.      void main()
  5640.      {
  5641.           int n;
  5642.      
  5643.           /* Create 1000 random elements */
  5644.           MKDATA();
  5645.      
  5646.           /* Initialise arbitrary data */
  5647.           INITDATA();
  5648.      
  5649.           /* Set last element indicator */
  5650.           lastone = 999;
  5651.      
  5652.           /* Call quick sort function */
  5653.           QKSORT();
  5654.      
  5655.           /* Initialise arbitrary data */
  5656.           INITDATA();
  5657.      
  5658.           /* Set last element indicator */
  5659.           lastone = 1000;
  5660.      
  5661.           /* Call shell sort function */
  5662.           SHELL();
  5663.      
  5664.           /* Initialise arbitrary data */
  5665.           INITDATA();
  5666.      
  5667.           /* Set last element indicator */
  5668.           lastone = 999;
  5669.      
  5670.           /* Call bubble sort function */
  5671.           BUBBLE();
  5672.      
  5673.           /* Initialise arbitrary data */
  5674.           INITDATA();
  5675.      
  5676.           /* Set last element indicator */
  5677.           lastone = 999;
  5678.      
  5679.           /* Call bubble sort with swap flag function */
  5680.           FBUBBLE();
  5681.      
  5682.           /* Initialise arbitrary data */
  5683.           INITDATA();
  5684.      
  5685.           /* Set last element indicator */
  5686.           lastone = 999;
  5687.      
  5688.           /* Call insertion sort function */
  5689.           INSERTION();
  5690.      }
  5691.  
  5692. And here are the profiler results for this program;
  5693.  
  5694. INTEGER SORTS - 1000 RANDOM NUMBERS (0 - 999)
  5695.  
  5696. FBUBBLE        3.7197 sec  41%
  5697. |********************************************
  5698. BUBBLE         3.5981 sec  39%
  5699. |******************************************
  5700. INSERTION      1.4258 sec  15% |***************
  5701. SHELL               0.1207 sec   1%  |*
  5702. QKSORT         0.0081 sec  <1% |
  5703.  
  5704. INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 999)
  5705.  
  5706. FBUBBLE        92.749 sec  42%
  5707. |********************************************
  5708. BUBBLE         89.731 sec  41%
  5709. |********************************************
  5710. INSERTION      35.201 sec  16% |***************
  5711. SHELL               0.9838 sec  <1% |
  5712. QKSORT         0.0420 sec  <1% |
  5713.  
  5714. INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 99)
  5715.  
  5716. FBUBBLE        92.594 sec  42% |*****************************************
  5717. BUBBLE         89.595 sec  40% |****************************************
  5718. INSERTION      35.026 sec  16% |***************
  5719. SHELL               0.7563 sec  <1% |
  5720. QKSORT         0.6018 sec  <1% |
  5721.  
  5722. INTEGER SORTS - 5000 RANDOM NUMBERS (0 - 9)
  5723.  
  5724. FBUBBLE        89.003 sec  41%
  5725. |*******************************************
  5726. BUBBLE         86.921 sec  40%
  5727. |*******************************************
  5728. INSERTION      31.544 sec  14% |***************
  5729. QKSORT         6.0358 sec   2%  |**
  5730. SHELL               0.5424 sec  <1% |
  5731.  
  5732. INTEGER SORTS - 5000 DESCENDING ORDERED NUMBERS
  5733.  
  5734. FBUBBLE        122.99 sec  39%
  5735. |******************************************
  5736. BUBBLE         117.22 sec  37% |****************************************
  5737. INSERTION      70.595 sec  22% |**********************
  5738. SHELL               0.6438 sec  <1% |
  5739. QKSORT         0.0741 sec  <1% |
  5740.  
  5741. INTEGER SORTS - 5000 ORDERED NUMBERS
  5742.  
  5743. BUBBLE         62.908 sec  99%
  5744. |******************************************
  5745. SHELL               0.3971 sec  <1% |
  5746. INSERTION      0.0510 sec  <1% |
  5747. QKSORT         0.0382 sec  <1% |
  5748. FBUBBLE        0.0251 sec  <1% |
  5749.  
  5750. INTEGER SORTS - 10000 RANDOM NUMBERS (0 - 999)
  5751.  
  5752. FBUBBLE        371.18 sec  42% |****************************************
  5753. BUBBLE         359.06 sec  41% |***************************************
  5754. INSERTION      140.88 sec  16% |**************
  5755. SHELL               2.0423 sec  <1% |
  5756. QKSORT         0.6183 sec  <1% |
  5757.  
  5758. Theory has it that the performance of a sorting algorithm is dependant
  5759. upon;
  5760.  
  5761.      a) the number of items to be sorted and
  5762.      b) how unsorted the list is to start with.
  5763.  
  5764. With this in mind it is worth testing the various sorting routines
  5765. described here to determine which one will best suit your particular
  5766. application. If you examine the above profiler results you will see that:
  5767.  
  5768.      1) With an already sorted list FBUBBLE() executes fastest
  5769.      
  5770.      2) With a random list of small variations between the values SHELL()
  5771.          executes fastest
  5772.      
  5773.      3) With a random list of large variations between the values
  5774.      QKSORT()
  5775.           executes the fastest
  5776.  
  5777. What the profiler does not highlight is that when the comparison aspect
  5778. of a sort function takes a disproportionately long time to execute in
  5779. relation to the rest of the sort function, then the bubble sort with a
  5780. swap flag will execute faster than the bubble sort with out a swap flag.
  5781.  
  5782. When considering a sort routine take into consideration memory
  5783. constraints and the type of data to be sorted as well as the relative
  5784. performances of the sort functions. Generally, the faster a sort
  5785. operates, the more memory it requires. Compare the simple bubble sort
  5786. with the quick sort, and you will see that the quick sort requires far
  5787. more memory than the bubble sort.
  5788.  
  5789.                                     
  5790.                         DYNAMIC MEMORY ALLOCATION
  5791.  
  5792.  
  5793. If a program needs a table of data, but the size of the table is
  5794. variable, perhaps for a list of all file names in the current directory,
  5795. it is inefficient to waste memory by declaring a data table of the
  5796. maximum possible size. Rather it is better to dynamically allocate the
  5797. table as required.
  5798.  
  5799. Turbo C allocates RAM as being available for dynamic allocation into an
  5800. area called the "heap". The size of the heap varies with memory model.
  5801. The tiny memory model defaults to occupy 64K of RAM. The small memory
  5802. model allocates upto 64K for the program/code and heap with a far heap
  5803. being available within the remainder of conventional memory. The other
  5804. memory models make all conventional memory available to the heap. This is
  5805. significant when programming in the tiny memory model when you want to
  5806. reduce the memory overhead of your program to a minimum. The way to do
  5807. this is to reduce the heap to a minimum size. The smallest is 1 byte.
  5808.  
  5809. C provides a function malloc() which allocates a block of free memory of
  5810. a specified size and returns a pointer to the start of the block; it also
  5811. provides free() which deallocates a block of memory previously allocated
  5812. by malloc(). Notice, however, that the IBM PC doesnot properly free
  5813. blocks of memory, and contiuous use of malloc() and free() will
  5814. fragmentise memory, eventually causing no memory to be available untill
  5815. the program terminates.
  5816.  
  5817. This program searches a specified file for a specified string (with case
  5818. sensitivity). It uses malloc() to allocate just enough memory for the
  5819. file to be read into memory.
  5820.  
  5821.      #include <stdio.h>
  5822.      #include <stdlib.h>
  5823.      
  5824.      char *buffer;
  5825.      
  5826.      void main(int argc, char *argv[])
  5827.      {
  5828.           FILE *fp;
  5829.           long flen;
  5830.      
  5831.           /* Check number of parameters */
  5832.           if (argc != 3)
  5833.           {
  5834.                fputs("Usage is sgrep <text> <file spec>",stderr);
  5835.                exit(0);
  5836.           }
  5837.      
  5838.           /* Open stream fp to file */
  5839.           fp = fopen(argv[2],"r");
  5840.           if (!fp)
  5841.           {
  5842.                perror("Unable to open source file");
  5843.                exit(0);
  5844.           }
  5845.      
  5846.           /* Locate file end */
  5847.           if(fseek(fp,0L,SEEK_END))
  5848.           {
  5849.                fputs("Unable to determine file length",stderr);
  5850.                fclose(fp);
  5851.                exit(0);
  5852.           }
  5853.      
  5854.           /* Determine file length */
  5855.           flen = ftell(fp);
  5856.      
  5857.           /* Check for error */
  5858.           if (flen == -1L)
  5859.           {
  5860.                fputs("Unable to determine file length",stderr);
  5861.                fclose(fp);
  5862.                exit(0);
  5863.           }
  5864.      
  5865.           /* Set file pointer to start of file */
  5866.           rewind(fp);
  5867.      
  5868.           /* Allocate memory buffer */
  5869.           buffer = malloc(flen);
  5870.      
  5871.           if (!buffer)
  5872.           {
  5873.                fputs("Unable to allocate memory",stderr);
  5874.                fclose(fp);
  5875.                exit(0);
  5876.           }
  5877.      
  5878.           /* Read file into buffer */
  5879.           fread(buffer,flen,1,fp);
  5880.      
  5881.           /* Check for read error */
  5882.           if(ferror(fp))
  5883.           {
  5884.                fputs("Unable to read file",stderr);
  5885.      
  5886.                /* Deallocate memory block */
  5887.                free(buffer);
  5888.      
  5889.                fclose(fp);
  5890.                exit(0);
  5891.           }
  5892.      
  5893.           printf("%s %s in %s",argv[1],(strstr(buffer,argv[1])) ? "was
  5894.      found" : "was not found",argv[2]);
  5895.      
  5896.           /* Deallocate memory block before exiting */
  5897.           free(buffer);
  5898.           fclose(fp);
  5899.      }
  5900.                                     
  5901.                          VARIABLE ARGUMENT LISTS
  5902.  
  5903.  
  5904. Some functions, such as printf(), accept a variable number and type of
  5905. arguments. C provides a mechanism to write your own functions which can
  5906. accept a variable argument list. This mechanism is the va_ family defined
  5907. in the header file `stdarg.h'.
  5908.  
  5909. There are three macros which allow implementation of variable argument
  5910. lists; va_start(), va_arg() and va_end() and a variable type va_list
  5911. which defines an array which holds the information required by the
  5912. macros.
  5913.  
  5914. va_start() takes two parameters, the first is the va_list variable and
  5915. the second is the last fixed parameter sent to the function. va_start()
  5916. must be called before attempting to access the variable argument list as
  5917. it sets up pointers required by the other macros.
  5918.  
  5919. va_arg() returns the next variable from the argument list. It is called
  5920. with two parameters, the first is the va_list variable and the second is
  5921. the type of the argument to be extracted. So, if the next variable in the
  5922. argument list is an integer, it can be extracted with;
  5923.  
  5924.  
  5925.      <int> = va_arg(<va_list>,int);
  5926.  
  5927. va_end() is called after extracting all required variables from the
  5928. argument list, and simply tidies up the internal stack if appropriate.
  5929. va_end() accepts a single parameter, the va_list variable.
  5930.  
  5931. The following simple example program illustrates the basis for a printf()
  5932. type implementation where the types of the arguments is not known, but
  5933. can be determined from the fixed parameter. This example only caters for
  5934. integer, string and character types, but could easily by extended to
  5935. cater for other variable types as well by following the method
  5936. illustrated;
  5937.  
  5938.      #include <stdarg.h>
  5939.      
  5940.      char *ITOS(long x, char *ptr)
  5941.      {
  5942.           /* Convert a signed decimal integer to a string */
  5943.      
  5944.           long pt[9] = { 100000000, 10000000, 1000000, 100000, 10000,
  5945.      1000, 100, 10, 1 };
  5946.           int n;
  5947.      
  5948.           /* Check sign */
  5949.           if (x < 0)
  5950.           {
  5951.                *ptr++ = '-';
  5952.                /* Convert x to absolute */
  5953.                x = 0 - x;
  5954.           }
  5955.      
  5956.           for(n = 0; n < 9; n++)
  5957.           {
  5958.                if (x > pt[n])
  5959.                {
  5960.                     *ptr++ = 48 + x / pt[n];
  5961.                     x %= pt[n];
  5962.                }
  5963.           }
  5964.           return(ptr);
  5965.      }
  5966.      
  5967.      void varfunc(char *format, ...)
  5968.      {
  5969.           va_list arg_ptr;
  5970.           char output[1000];
  5971.           char *ptr;
  5972.           int bytes;
  5973.           int x;
  5974.           char *y;
  5975.           char z;
  5976.      
  5977.           /* Initialise pointer to argument list */
  5978.           va_start(arg_ptr, format);
  5979.      
  5980.           /* loop format string */
  5981.           ptr = output;
  5982.           bytes = 0;
  5983.           while(*format)
  5984.           {
  5985.                /* locate next argument */
  5986.                while(*format != '%')
  5987.                {
  5988.                     *ptr++ = *format++;
  5989.                     bytes++;
  5990.                }
  5991.                /* A % has been located */
  5992.                format++;
  5993.                switch(*format)
  5994.                {
  5995.                     case '%' :  *ptr++ = '%';
  5996.                              break;
  5997.      
  5998.                     case 'd' : /* integer expression follows */
  5999.                             x = va_arg(arg_ptr,int);
  6000.                             ptr = ITOS(x,ptr);
  6001.                             *ptr = 0;
  6002.                             format++;
  6003.                             bytes += strlen(output) - bytes;
  6004.                             break;
  6005.      
  6006.                     case 's' : /* String expression follows */
  6007.                             y = va_arg(arg_ptr,char *);
  6008.                             strcat(output,y);
  6009.                             x = strlen(y);
  6010.                             format++;
  6011.                             ptr += x;
  6012.                             bytes += x;
  6013.                             break;
  6014.      
  6015.                     case 'c' : /* Char expression follows */
  6016.                             z = va_arg(arg_ptr,char);
  6017.                             *ptr++ = z;
  6018.                             format++;
  6019.                             bytes++;
  6020.                             break;
  6021.                }
  6022.      
  6023.           }
  6024.      
  6025.           /* Clean stack just in case! */
  6026.           va_end(arg_ptr);
  6027.      
  6028.           /* Null terminate output string */
  6029.           *ptr = 0;
  6030.      
  6031.           /* Display what we got */
  6032.           printf("\nOUTPUT==%s",output);
  6033.      }
  6034.      
  6035.      void main()
  6036.      {
  6037.           varfunc("%d %s %c",5,"hello world",49);
  6038.      }
  6039.      
  6040.  
  6041. A simpler variation is to use vsprintf() rather than implementing our own
  6042. variable argument list access. However, it is beneficial to understand
  6043. how variable argument lists behave. The following is a simplification of
  6044. the same program, but leaving the dirty work to the compiler;
  6045.  
  6046.      #include <stdio.h>
  6047.      #include <stdarg.h>
  6048.      
  6049.      void varfunc(char *format, ...)
  6050.      {
  6051.           va_list arg_ptr;
  6052.           char output[1000];
  6053.      
  6054.           va_start(arg_ptr, format);
  6055.      
  6056.           vsprintf(output,format,arg_ptr);
  6057.      
  6058.           va_end(arg_ptr);
  6059.      
  6060.           /* Display what we got */
  6061.           printf("\nOUTPUT==%s",output);
  6062.      }
  6063.      
  6064.      void main()
  6065.      {
  6066.           varfunc("%d %s %c",5,"hello world",49);
  6067.      }
  6068.                                     
  6069.                          TRIGONOMETRY FUNCTIONS
  6070.  
  6071. The ANSI standard on C defines a number of trigonometry functions, all of
  6072. which accept an angle parameter in radians;
  6073.  
  6074.  
  6075. FUNCTION  PROTOTYPE                DESCRIPTION
  6076. acos      double acos(double x)              arc cosine of x
  6077. asin      double asin(double x)              arc sine of x
  6078. atan      double atan(double x)              arc tangent of x
  6079. atan2          double atan2(double x, double y)        arc tangent of y/x
  6080. cos       double cos(double x)               cosine of x
  6081. cosh      double cosh(double x)              hyperbolic cosine of x
  6082. sin       double sin(double x)               sine of x
  6083. sinh      double sinh(double x)              hyperbolic sine of x
  6084. tan       double tan(double x)               tangent of x
  6085. tanh      double tanh(double x)              hyperbolic tangent of x
  6086.  
  6087. There are 2PI radians in a circle, therefore 1 radian is equal to 360/2PI
  6088. degrees or approximately 57 degrees in 1 radian. The calculation of any
  6089. of the above functions requires large floating point numbers to be used
  6090. which is a very slow process. If you are going to use calls to a trig'
  6091. function, it is a good idea to use a lookup table of values rather than
  6092. keep on calling the function. This approach is used in the discussion on
  6093. circle drawing later in this book.
  6094.  
  6095.                                     
  6096.                                  ATEXIT
  6097.  
  6098.  
  6099. When ever a program terminates, it should close any open files (this is
  6100. done for you by the C compiler's startup/termination code which it
  6101. surrounds your program with), and restore the host computer to some
  6102. semblance of order.  Within a large program where exit may occur from a
  6103. number of locations it is a pain to have to keep on writing calls to the
  6104. cleanup routine. Fortunately we don't have to!
  6105.  
  6106. The ANSI standard on C describes a function, atexit(), which registers
  6107. the specified function, supplied as a parameter to atexit(), as a
  6108. function which is called immediately before terminating the program. This
  6109. function is called automatically, so the following program calls
  6110. `leave()' whether an error occurs or not;
  6111.  
  6112.      #include <stdio.h>
  6113.      
  6114.      void leave()
  6115.      {
  6116.           puts("\nBye Bye!");
  6117.      }
  6118.      
  6119.      void main()
  6120.      {
  6121.           FILE *fp;
  6122.           int a;
  6123.           int b;
  6124.           int c;
  6125.           int d;
  6126.           int e;
  6127.           char text[100];
  6128.      
  6129.           atexit(leave);
  6130.      
  6131.           fp = fopen("data.txt","w");
  6132.      
  6133.           if(!fp)
  6134.           {
  6135.                perror("Unable to create file");
  6136.                exit(0);
  6137.           }
  6138.      
  6139.           fprintf(fp,"1 2 3 4 5 \"A line of numbers\"");
  6140.      
  6141.           fflush(fp);
  6142.      
  6143.           if (ferror(fp))
  6144.           {
  6145.                fputs("Error flushing stream",stderr);
  6146.                exit(1);
  6147.           }
  6148.      
  6149.           rewind(fp);
  6150.           if (ferror(fp))
  6151.           {
  6152.                fputs("Error rewind stream",stderr);
  6153.                exit(1);
  6154.           }
  6155.      
  6156.           fscanf(fp,"%d %d %d %d %d %s",&a,&b,&c,&d,&e,text);
  6157.           if (ferror(fp))
  6158.           {
  6159.                /* Unless you noticed the deliberate bug earlier */
  6160.                /* The program terminates here */
  6161.                fputs("Error reading from stream",stderr);
  6162.                exit(1);
  6163.           }
  6164.      
  6165.           printf("\nfscanf() returned %d %d %d %d %d %s",a,b,c,d,e,text);
  6166.      }
  6167.                                     
  6168.                             INCREASING SPEED
  6169.  
  6170.  
  6171. In order to reduce the time your program spends executing it is essential
  6172. to know your host computer. Most computers are very slow at displaying
  6173. information on the screen. And the IBM PC is no exception to this. C
  6174. offers various functions for displaying data, printf() being one of the
  6175. most commonly used and also the slowest. Whenever possible try to use
  6176. puts(varname) in place of printf("%s\n",varname). Remembering that puts()
  6177. appends a newline to the string sent to the screen.
  6178.  
  6179. When multiplying a variable by a constant which is a factor of 2 many C
  6180. compilers will recognise that a left shift is all that is required in the
  6181. assembler code to carry out the multiplication rapidly. When multiplying
  6182. by other values it is often faster to do a multiple addition instead, so;
  6183.  
  6184.  
  6185.      'x * 3' becomes 'x + x + x'
  6186.  
  6187. Don't try this with variable multipliers in a loop because it becomes
  6188. very slow! But, where the multiplier is a constant it can be faster.
  6189. (Sometimes!) Another way to speed up multiplication and division is with
  6190. the shift commands, << and >>.
  6191.  
  6192. The instruction x /= 2 can equally well be written x >>= 1, shift the
  6193. bits of x right one place. Many compilers actually convert integer
  6194. divisions by 2 into a shift right instruction. You can use the shifts for
  6195. multiplying and dividing by 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 &c.
  6196. If you have difficulty understanding the shift commands consider the
  6197. binary form of a number;
  6198.  
  6199.  
  6200.      01001101  equal to   77
  6201.  
  6202. shifted right one place it becomes;
  6203.  
  6204.      00100110  equal to   38
  6205.  
  6206. Try to use integers rather than floating point numbers where ever
  6207. possible.  Sometimes you can use integers where you didn't think you
  6208. could! For example, to convert a fraction to a decimal one would normally
  6209. say;
  6210.  
  6211.      percentage = x / y * 100
  6212.  
  6213. This requires floating point variables. However, it can also be written
  6214. as;
  6215.  
  6216.      z = x * 100;
  6217.      percentage = z / y
  6218.  
  6219. Which works fine with integers, so long as you don't mind the percentage
  6220. being truncated. eg;
  6221.  
  6222.      5 / 7 * 100 is equal to 71.43 with floating point
  6223.  
  6224. but with integers;
  6225.  
  6226.      5 * 100 / 7 is equal to 71
  6227.  
  6228. (Assuming left to right expression evaluation. You may need to force the
  6229. multiplication to be done first as with `z = x * 100').
  6230.  
  6231. Here is a test program using this idea;
  6232.  
  6233.      float funca(double x, double y)
  6234.      {
  6235.           return(x / y * 100);
  6236.      }
  6237.      
  6238.      int funcb(int x,int y)
  6239.      {
  6240.           return(x * 100 / y);
  6241.      }
  6242.      
  6243.      void main()
  6244.      {
  6245.           int n;
  6246.           double x;
  6247.           int y;
  6248.      
  6249.           for(n = 0; n < 5000; n++)
  6250.           {
  6251.                x = funca(5,7);
  6252.                y = funcb(5,7);
  6253.           }
  6254.      }
  6255.      
  6256. And here is the results of the test program fed through a profiler;
  6257.  
  6258. funca            1.9169 sec  96%
  6259. |**********************************************
  6260. funcb            0.0753 sec   3%  |*
  6261.  
  6262. You can clearly see that the floating point function is 25 times slower
  6263. than the integer equivalent!
  6264.  
  6265. NB: Although it is normal practice for expressions to be evaluated left
  6266. to right, the ANSI standard on C does not specify an order of preference
  6267. for expression evaluation, and as such you should check your compiler
  6268. manual.
  6269.  
  6270. Another way of increasing speed is to use pointers rather than array
  6271. indexing. When you access an array through an index, for example with;
  6272.  
  6273.      x = data[i];
  6274.  
  6275. the compiler has to calculate the offset of data[i] from the beginning of
  6276. the array. A slow process. Using pointers can often improve things as the
  6277. following two bubble sorts, one with array indexing and one with pointers
  6278. illustrates;
  6279.  
  6280.      void BUBBLE()
  6281.      {
  6282.           /* Bubble sort using array indexing */
  6283.      
  6284.           int a;
  6285.           int b;
  6286.           int temp;
  6287.      
  6288.           for(a = lastone; a >= 0; a--)
  6289.           {
  6290.                for(b = 0; b < a; b++)
  6291.                {
  6292.                     if(data[b] > data[b + 1])
  6293.                     {
  6294.                          temp = data[b];
  6295.                          data[b] = data[b + 1];
  6296.                          data[b + 1] = temp;
  6297.                     }
  6298.                }
  6299.           }
  6300.      }
  6301.      
  6302.      void PTRBUBBLE()
  6303.      {
  6304.           /* Bubble sort using pointers */
  6305.      
  6306.           int temp;
  6307.           int *ptr;
  6308.           int *ptr2;
  6309.      
  6310.           for(ptr = &data[lastone]; ptr >= data; ptr--)
  6311.           {
  6312.                for(ptr2 = data; ptr2 < ptr; ptr2++)
  6313.                {
  6314.                     if(*ptr2 > *(ptr2 + 1))
  6315.                     {
  6316.                          temp = *ptr2;
  6317.                          *ptr2 = *(ptr2 + 1);
  6318.                          *(ptr2 + 1) = temp;
  6319.                     }
  6320.                }
  6321.           }
  6322.      }
  6323.      
  6324. Here are the profiler results for the two versions of the same bubble
  6325. sort operating on the same 1000 item, randomly sorted list;
  6326.  
  6327. BUBBLE       3.1307 sec  59% |******************************************
  6328. PTRBUBBLE    2.1686 sec  40% |***************************
  6329.  
  6330.  
  6331. Here is another example of how to initialise an array using first the
  6332. common indexing approach, and secondly the pointer approach;
  6333.  
  6334.      /* Index array initialisation */
  6335.      int n;
  6336.      
  6337.      for(n = 0; n < 1000; n++)
  6338.           data[n] = random(1000);
  6339.      
  6340.      
  6341.      /* Pointer array initialisation */
  6342.      int *n;
  6343.      
  6344.      for(n = data; n < &data[1000]; n++)
  6345.           *n = random(1000);
  6346.      
  6347.  
  6348. Needless to say, the pointer approach is faster than the index. The
  6349. pointer approach is only really of benefit when an array is going to be
  6350. traversed, as in the above examples. In the case of say a binary search
  6351. where a different and non-adjacent element is going to be tested each
  6352. pass then the pointer approach is no better than using array indexing.
  6353.  
  6354. The exception to this rule of using pointers rather than indexed access,
  6355. comes with pointer to pointers. Say your program has declared a table of
  6356. static data, such as:
  6357.  
  6358. static char *colours[] = { "Black", "Blue", "Green", "Yellow", "Red",
  6359. "White" };
  6360.  
  6361. It is faster to access the table with colours[n] than it is with a
  6362. pointer, since each element in the table colours[], is a pointer. If you
  6363. need to scan a string table for a value you can use this very fast
  6364. approach instead;
  6365.  
  6366. First the table is changed into a single string, with some delimiter
  6367. between the elements.
  6368.  
  6369.      static char *colours = "Black/Blue/Green/Yellow/Red/White";
  6370.  
  6371. Then to confirm that a value is held in the table you can use strstr();
  6372.  
  6373.      result = strstr(colours,"Cyan");
  6374.  
  6375. Using in-line assembler code can provide the greatest speed increase.
  6376. Care must be taken however not to interfere with the compiled C code. It
  6377. is usually safe to write a complete function with in-line assembler code,
  6378. but mixing in-line assembler with C code can be hazardous. As a rule of
  6379. thumb, get your program working without assembler code, and then if you
  6380. want to use in-line assembler, convert small portions of the code at a
  6381. time, testing the program at each stage. Video I/O is a very slow process
  6382. with C, and usually benefits from in-line assembler, and we have used
  6383. this principle quite widely in the example programs which follow later.
  6384.  
  6385.                                     
  6386.                                PC GRAPHICS
  6387.  
  6388. When programming graphics you should bear in mind that they are a machine
  6389. dependant subject. Code to produce graphics on an IBM PC will not port to
  6390. an Amiga, or VAX or any other type of computer.
  6391.  
  6392.  
  6393.  
  6394. Introduction To PC Graphics
  6395.  
  6396. The IBM PC and compatible range of computers display information on a
  6397. visual display unit (VDU). To enable the computer to send information to
  6398. the VDU a component is included within the computer called a "display
  6399. card". There are various display cards and VDUs which have been produced
  6400. for the IBM PC range of computers; monochrome display adapter (MDA),
  6401. colour graphics adapter (CGA), Hercules graphics card (HGC), Enhanced
  6402. graphics adapter (EGA), video graphics array (VGA), super video graphics
  6403. array (SVGA), memory controller gate array (MCGA), 8514/A and the Txas
  6404. Instruments Graphics Architecture (TIGA). For simplicity, this section
  6405. will concern itself only with the three more common types of display;
  6406.  
  6407.      CGA, EGA and VGA
  6408.  
  6409. Information about the VGA display is also relevant to the SVGA display
  6410. which in simple terms can do the same and more. This section will not
  6411. concern itself with monochrome displays since they are of limited use in
  6412. graphics work.
  6413.  
  6414.  
  6415. Display Modes
  6416.  
  6417. When an IBM PC computer is first switched on is set to display 80 columns
  6418. by 25 rows of writing. This measurement, 80 x 25 is called the
  6419. "resolution". A display mode which is intended for displaying writing is
  6420. called a "text" mode.  Where as a display mode which is intended for
  6421. displaying pictures is called a "graphics" mode.
  6422.  
  6423. If you look closely at the display you will see that each displayed
  6424. character is comprised of dots. In reality the entire display is
  6425. comprised of dots, called "pixels", which may be set to different
  6426. colours. In text display modes these pixels are not very relevant,
  6427. however, in graphics display modes each pixel may be selected and set by
  6428. a program. The size of the pixels varies with the display resolution. In
  6429. 80 x 25 text mode the pixels are half the width they are in 40 x 25 text
  6430. display mode.
  6431.  
  6432. Depending upon the display card installed in the computer, there are a
  6433. number of display modes which may be used;
  6434.  
  6435.  
  6436. MODE   TYPE               RESOLUTION         COLOURS
  6437.                                              
  6438.  0       Text             40 x 25            4 (CGA), 16 (EGA,
  6439.                                              VGA) Shades of
  6440.                                              grey
  6441.  1       Text             40 x 25            4 (CGA), 16 (EGA,
  6442.                                              VGA)
  6443.  2       Text             80 x 25            4 (CGA), 16 (EGA,
  6444.                                              VGA) Shades of
  6445.                                              grey
  6446.  3       Text             80 x 25            4 (CGA), 16 (EGA,
  6447.                                              VGA)
  6448.  4       Graphics         320 x 200          4
  6449.  5       Graphics         320 x 200          4 (grey on CGA
  6450.                                              and EGA)
  6451.  6       Graphics         640 x 200          2
  6452.  7       Text             80 x 25            Mono (EGA, VGA)
  6453. 13       Graphics         320 x 200          16 (EGA, VGA)
  6454. 14       Graphics         640 x 200          16 (EGA, VGA)
  6455. 15       Graphics         640 x 350          Mono (EGA, VGA)
  6456. 16       Graphics         640 x 350          16 (EGA, VGA)
  6457. 17       Graphics         640 x 480          2 (VGA)
  6458. 18       Graphics         640 x 480          16 (VGA)
  6459. 19       Graphics         320 x 200          256 (VGA)
  6460.  
  6461. The term resolution in graphics modes refers to the number of pixels
  6462. across and down the VDU. The larger the number of pixels, the smaller
  6463. each is and the sharper any displayed image appears. As you can see from
  6464. the table, the VGA display can produce a higher resolution than the other
  6465. display cards, resulting in sharper images being produced.
  6466.  
  6467.  
  6468. The CGA display card can produce a maximum resolution of 320 x 200
  6469. pixels, where as the VGA display card can produce a resolution of 640 x
  6470. 480 pixels.  This is why writing on a VGA VDU looks so much sharper than
  6471. the writing displayed on a CGA VDU.
  6472.  
  6473.  
  6474.  
  6475. Accessing The Display
  6476.  
  6477. Inside the IBM PC computer is a silicon chip called the BIOS ROM, this
  6478. chip contains functions which may be called by an external computer
  6479. program to access the display card, which in turn passes the information
  6480. on to the VDU.  The BIOS display functions are all accessed by generating
  6481. interrupt 10 calls, with the number of the appropriate function stored in
  6482. the assembly language AH register.
  6483.  
  6484. A programmer interested in creating graphics displays must first be able
  6485. to switch the display card to an appropriate graphics mode. This is
  6486. achieved by calling the BIOS display function 0, with th number of the
  6487. desired display mode from the table stored in the assembly language AL
  6488. register thus the following assembly language code segment will switch
  6489. the display card to CGA graphics mode 4, assuming that is that the
  6490. display card is capable of this mode;
  6491.  
  6492.           mov   ah , 00
  6493.           mov   al , 04
  6494.           int   10h
  6495.  
  6496.  
  6497. A C function for selecting video display modes can be written;
  6498.  
  6499.      #include <dos.h>
  6500.      
  6501.      void setmode(unsigned char mode)
  6502.      {
  6503.           /* Sets the video display mode */
  6504.      
  6505.           union REGS inregs outreg;
  6506.      
  6507.           inreg.h.ah = 0;
  6508.           inreg.h.al = mode;
  6509.           int86(0x10,&inreg,&outregs);
  6510.      }
  6511.      
  6512. Any graphics are created by setting different pixels to different
  6513. colours, this is termed "plotting", and is achieved by calling BIOS
  6514. display function 12 with the pixel's horizontal coordinate in the
  6515. assembly language CX register and it's vertical coordinate in the
  6516. assembly language DX register and the required colour in the assembly
  6517. language AL register thus;
  6518.  
  6519.  
  6520.           mov   ah, 12
  6521.           mov   al, colour
  6522.           mov   bh, 0
  6523.           mov   cx, x_coord
  6524.           mov   dx, y_coord
  6525.           int   10h
  6526.  
  6527. The corresponding C function is;
  6528.  
  6529.      #include <dos.h>
  6530.      
  6531.      void plot(int x_coord, int y_coord, unsigned char colour)
  6532.      {
  6533.           /* Sets the colour of a pixel */
  6534.      
  6535.           union REGS regs;
  6536.      
  6537.           regs.h.ah = 12;
  6538.           regs.h.al = colour;
  6539.           regs.h.bh = 0;
  6540.           regs.x.cx = x_coord;
  6541.           regs.x.dx = y_coord;
  6542.           int86(0x10,®s,®s);
  6543.      }
  6544.  
  6545. The inverse function of plot is to read the existing colour setting of a
  6546. pixel. This is done by calling BIOS ROM display function 13, again with
  6547. the pixel's horizontal coordinate in the assembly language CX register
  6548. and it's vertical coordinate in the assembly language DX register. This
  6549. function then returns the pixel's colour in the assembly language AL
  6550. register;
  6551.  
  6552.  
  6553.      #include <dos.h>
  6554.      
  6555.      unsigned char get_pixel(int x_coord, int y_coord)
  6556.      {
  6557.           /* Reads the colour of a pixel */
  6558.      
  6559.           union REGS inreg, outreg;
  6560.      
  6561.           inreg.h.ah = 13;
  6562.           inreg.h.bh = 0;
  6563.           inreg.x.cx = x_coord;
  6564.           inreg.x.dx = y_coord;
  6565.           int86(0x10,&inreg,&outreg);
  6566.           return(outreg.h.al);
  6567.      }
  6568.  
  6569.  
  6570. Colour And The CGA
  6571.  
  6572. The CGA display card can display a maximum of 4 colours simultaneously at
  6573. any time. However, the display card can generate a total of 8 colours.
  6574. There are two sets of colours, called "palettes". The first palette
  6575. contains the colours;
  6576.  
  6577.      background, cyan, magenta and white.
  6578.  
  6579. the second palette contains the colours;
  6580.  
  6581.        background, green, red and yellow.
  6582.  
  6583. Colour 0 is always the same as the background colour.
  6584.  
  6585. The pixels displayed on the VDU take their colours from the currently
  6586. active palette, and are continuously being refreshed. So, if the active
  6587. palette changes, so to do the colours of the displayed pixels on the VDU.
  6588.  
  6589. Selection of the active CGA palette is achieved by calling the BIOS
  6590. display function 11 with the number of the desired palette (either 0 or
  6591. 1) in the assembly language BH register;
  6592.  
  6593.  
  6594.           mov   ah, 11
  6595.           mov   bh, palette
  6596.           int   10h
  6597.  
  6598. The C function for selecting the CGA palette is;
  6599.  
  6600.      void palette(unsigned char palette)
  6601.      {
  6602.           union REGS inreg, outreg;
  6603.      
  6604.           inreg.h.ah = 11;
  6605.           inreg.h.bh = palette;
  6606.           int86(0x10,&inreg,&outreg);
  6607.      }
  6608.      
  6609. The background colour may be selected independantly from any of the eight
  6610. available colours by calling the same BIOS display function with a value
  6611. of 0 stored in the assembly language BH register and the desired
  6612. background colour in the assembly language BL register;
  6613.  
  6614.  
  6615.      mov   ah, 11
  6616.      mov   bh, 0
  6617.      mov   bl, colour
  6618.      int   10h
  6619.  
  6620. In C this function can be written;
  6621.  
  6622.      void background(unsigned char colour)
  6623.      {
  6624.           union REGS inreg, outreg;
  6625.      
  6626.           inreg.h.ah = 11;
  6627.           inreg.h.bh = 0;
  6628.           inreg.h.bl = colour;
  6629.           int86(0x10,&inreg,&outreg);
  6630.      }
  6631.      
  6632. The background colours available are;
  6633.  
  6634.      0       Black
  6635.      1       Blue
  6636.      2       Green
  6637.      3       Cyan
  6638.      4       Red
  6639.      5       Magenta
  6640.      6       Yellow
  6641.      7       White
  6642.  
  6643.  
  6644. Colour And The EGA
  6645.  
  6646. The EGA display card can display a maximum of 16 colours simultaneously
  6647. at any time. The colour of all pixels is continuously refreshed by the
  6648. display card by reading the colour from the EGA palette. Unlike the CGA
  6649. display card, the EGA display card allows you to redefine any or all of
  6650. the colours in the palette. Unfortunately only the first 8 colours may be
  6651. loaded into other palette colours.
  6652.  
  6653. The colours are;
  6654.  
  6655.      0    Black
  6656.      1    Blue
  6657.      2    Green
  6658.      3    Cyan
  6659.      4    Red
  6660.      5    Magenta
  6661.      6    Brown
  6662.      7    Light grey
  6663.      8    Dark grey
  6664.      9    Light blue
  6665.      10        Light green
  6666.      11   Light cyan
  6667.      12        Light red
  6668.      13        Light magenta
  6669.      14        Yellow
  6670.      15        White
  6671.  
  6672. Changing a palette colour is achieved by calling the BIOS display
  6673. function 16 with a value of 0 in the assembly language AL register, the
  6674. colour value (0 to 7) in the assembly language BH register and the number
  6675. of the palette colour (0 to 15) in the assembly language BL register
  6676. thus;
  6677.  
  6678.  
  6679.      mov   ah,16
  6680.      mov   al,0
  6681.      mov   bl,palette
  6682.      mov   bh,colour
  6683.      int   10h
  6684.  
  6685. In C this function may be written;
  6686.  
  6687.      void ega_palette(unsigned char colour, unsigned char palette)
  6688.      {
  6689.           union REGS inreg,outreg;
  6690.      
  6691.           inreg.h.ah = 16;
  6692.           inreg.h.al = 0;
  6693.           inreg.h.bl = palette;
  6694.           inreg.h.bh = colour;
  6695.      
  6696.           int86(0x10,&inreg,&outreg);
  6697.      }
  6698.  
  6699.  
  6700.  
  6701. Colour And The VGA
  6702.  
  6703. The VGA display card can display a maximum of 256 colours on the VDU at
  6704. any one time, these colours are defined by information held in 256
  6705. special registers called "DAC" registers. As with the CGA and EGA
  6706. displays, the colour of displayed pixels is continuously being refreshed,
  6707. and as such any change to a DAC register is immediately visible. Each DAC
  6708. register has three component data parts which record the amount of green,
  6709. blue and red colours which make up the displayed colour. Each of these
  6710. seperate data components can hold a value between 0 and 63 giving the VGA
  6711. display card the ability to display 262,144 colours! Although only a
  6712. small subset of them can be displayed at any one time.
  6713.  
  6714. Setting the value of a DAC register is achieved by calling the BIOS
  6715. display function 16 with a value of 16 stored in the assembly language AL
  6716. register, the green value stored in the assembly language CH register,
  6717. the blue value stored in the assembly language CL register and the red
  6718. value stored in the assembly language DH register and the number of the
  6719. DAC register to be set stored in the assembly language BX register;
  6720.  
  6721.  
  6722.           mov   ah,16
  6723.           mov   al,16
  6724.           mov   ch,green
  6725.           mov   cl,blue
  6726.           mov   dh,red
  6727.           mov   bx,dac
  6728.           int   10h
  6729.  
  6730.  
  6731. The C function to set a DAC register looks lik this;
  6732.  
  6733.      void set_dac(int dac, unsigned char green, unsigned char blue,
  6734.      unsigned char red)
  6735.      {
  6736.           union REGS regs;
  6737.      
  6738.           regs.h.ah = 16;
  6739.           regs.h.al = 16;
  6740.           regs.x.bx = dac;
  6741.           regs.h.ch = green;
  6742.           regs.h.cl = blue;
  6743.           regs.h.dh = red;
  6744.           int86(0x10,®s,®s);
  6745.      }
  6746.      
  6747.  
  6748.  
  6749. Displaying Text
  6750.  
  6751. The BIOS ROM provides three functions for displaying a single character.
  6752. The first function to consider is the one used extensively by DOS for
  6753. displaying messages, this is function 14 called "write text in teletype
  6754. mode". This function interprets some control characters; bell (ascii 7),
  6755. backspace (ascii 8), carriage return (ascii 10) and line feed (ascii 13)
  6756. but all other ascii codes are displayed, and the current cursor position
  6757. updated accordingly, moving down a row when a character is displayed in
  6758. the far right column. To call this function the assembly language
  6759. register AL holds the ascii code of the character to be displayed and
  6760. assembly language register BL holds the foreground colour for the
  6761. character to be displayed in if a graphics mode is active;
  6762.  
  6763.  
  6764.           mov   ah,14
  6765.           mov   al,character
  6766.           mov   bh,0
  6767.           mov   bl,foreground
  6768.           int   10h
  6769.  
  6770.  
  6771. A C function for accessing the write text in teletype mode may be written
  6772. like this;
  6773.  
  6774.      #include <dos.h>
  6775.      
  6776.      void teletype(unsigned char character, unsigned char foreground)
  6777.      {
  6778.           union REGS inreg, outreg;
  6779.      
  6780.           inreg.h.ah = 14;
  6781.           inreg.h.al = character;
  6782.           inreg.h.bh = 0;
  6783.           inreg.h.bl = foreground;
  6784.           int86(0x10,&inrg,&outreg);
  6785.      }
  6786.      
  6787. The second BIOS ROM display function for displaying a character allows
  6788. the foreground and background colours of the displayed character to be
  6789. defined. It also allows multiple copies of the character to be displayed
  6790. one after another automatically displaying subsequent characters at the
  6791. next display position, although the current cursor position is not
  6792. changed by this function.
  6793.  
  6794. This function is called "write character and attribute", and is BIOS ROM
  6795. display function number 9. It is called with the ascii code of the
  6796. character to be displayed in the assembly language AL register, the
  6797. display page in assembly language register BH, the foreground colour in
  6798. the first four bits of the assembly language register BL and the
  6799. background colour in the last four bits of the assembly language register
  6800. BL, the number of times the character is to be displayed is stored in the
  6801. assembly language CX register thus;
  6802.  
  6803.  
  6804.           mov   ah,9
  6805.           mov   al,character
  6806.           mov   bh,0
  6807.           mov   bl,foreground + 16 * background
  6808.           mov   cx,number
  6809.           int   10h
  6810.  
  6811. And in C;
  6812.  
  6813.      #include <dos.h>
  6814.      
  6815.      void char_attrib(unsigned char character, unsigned char foreground,
  6816.      unsigned char background, int number)
  6817.      {
  6818.           union REGS inreg,outreg;
  6819.      
  6820.           inreg.h.ah = 9;
  6821.           inreg.h.al = character;
  6822.           inreg.h.bh = 0;
  6823.           inreg.h.bl = (background << 4) + foreground;
  6824.           inreg.x.cx = number;
  6825.           int86(0x10,&inreg,&outreg);
  6826.      }
  6827.  
  6828. The last BIOS ROM display function for displaying a character retains the
  6829. foreground and background colours of the display position.
  6830.  
  6831. This function is called "write character", and is BIOS ROM display
  6832. function number 10. It is identical to BIOS ROM display function 9 except
  6833. that the colours of the displayed character are those which are prevalent
  6834. at the display position, except in graphics modes when the foreground
  6835. colour of the character is determined by the value in the assembly
  6836. language BL register.  Its use is as follows;
  6837.  
  6838.  
  6839.           mov   ah,10
  6840.           mov   al,character
  6841.           mov   bh,0
  6842.           mov   bl,foreground   ; For graphics modes ONLY
  6843.           mov   cx,number
  6844.           int   10h
  6845.  
  6846. And in C;
  6847.  
  6848.      #include <dos.h>
  6849.      
  6850.      void char_attrib(unsigned char character, unsigned char foreground,
  6851.      int number)
  6852.      {
  6853.           union REGS inreg,outreg;
  6854.      
  6855.           inreg.h.ah = 10;
  6856.           inreg.h.al = character;
  6857.           inreg.h.bh = 0;
  6858.           inreg.h.bl = foreground; /* For graphics modes ONLY */
  6859.           inreg.x.cx = number;
  6860.           int86(0x10,&inreg,&outreg);
  6861.      }
  6862.      
  6863.  
  6864. Positioning of the text cursor is provided for by the ROM BIOS display
  6865. function number 2. It is called with the row number in the assembly
  6866. language register DH and the column number in the assembly language
  6867. register DL;
  6868.  
  6869.           mov   ah,2
  6870.           mov   bh,0
  6871.           mov   dh,row
  6872.           mov   dl,column
  6873.           int   10h
  6874.  
  6875. The corresponding function in C looks like this;
  6876.  
  6877.      #include <dos.h>
  6878.      
  6879.      void at(unsigned char row, unsigned char column)
  6880.      {
  6881.           union REGS regs;
  6882.      
  6883.           regs.h.ah = 2;
  6884.           regs.h.bh = 0;
  6885.           regs.h.dh = row;
  6886.           regs.h.dl = column;
  6887.           int86(0x10,®s,®s);
  6888.      }
  6889.  
  6890. From these basic functions a more useful replacement for the C language's
  6891. "printf()" function can be written which allows data to be displayed at
  6892. the current cursor position, previously set by a call to "at()", with
  6893. prescribed attributes;
  6894.  
  6895.  
  6896.      #include <dos.h>
  6897.      #include <stdarg.h>
  6898.      
  6899.      void at(unsigned char row, unsigned char column)
  6900.      {
  6901.           union REGS regs;
  6902.      
  6903.           regs.h.ah = 2;
  6904.           regs.h.bh = 0;
  6905.           regs.h.dh = row;
  6906.           regs.h.dl = column;
  6907.           int86(0x10,®s,®s);
  6908.      }
  6909.      
  6910.      void xprintf(unsigned char foreground, unsigned char background,
  6911.      char *format,...)
  6912.      {
  6913.           union REGS inreg,outreg;
  6914.      
  6915.           va_list arg_ptr;
  6916.           static char output[1000];
  6917.           unsigned char col;
  6918.           unsigned char row;
  6919.           unsigned char n;
  6920.           unsigned char p;
  6921.           unsigned char text;
  6922.           unsigned char attr;
  6923.      
  6924.           /* Convert foreground and background colours into a single
  6925.      attribute */
  6926.           attr = (background << 4) + foreground;
  6927.      
  6928.           /* Copy data into a single string */
  6929.           va_start(arg_ptr, format);
  6930.           vsprintf(output, format, arg_ptr);
  6931.      
  6932.           /* Determine number of display columns */
  6933.           inreg.h.ah = 15;
  6934.           int86(0x10,&inreg,&outreg);
  6935.           n = outreg.h.ah;
  6936.      
  6937.           /* Determine current cursor position */
  6938.           inreg.h.ah = 3;
  6939.           inreg.h.bh = 0;
  6940.           int86(0x10,&inreg,&outreg);
  6941.           row = outreg.h.dh;
  6942.           col = outreg.h.dl;
  6943.      
  6944.           /* Now display data */
  6945.           p = 0;
  6946.           while (output[p])
  6947.           {
  6948.                /* Display this character */
  6949.                inreg.h.bh = 0;
  6950.                inreg.h.bl = attr;
  6951.                inreg.x.cx = 01;
  6952.                inreg.h.ah = 9;
  6953.                inreg.h.al = output[p++];
  6954.                int86(0x10,&inreg,&outreg);
  6955.      
  6956.                /* Update cursor position */
  6957.                /* moving down a row if required */
  6958.                col++;
  6959.                if (col < (n - 1))
  6960.                     at(row, col);
  6961.                else
  6962.                {
  6963.                     col = 0;
  6964.                     at(++row, col);
  6965.                }
  6966.           }
  6967.      }
  6968.      
  6969. This function, "xprintf()" illustrates two more functions of the BIOS
  6970. ROM. The first is the call to function 15 which returns the number of
  6971. text display columns for the currently active display mode.
  6972.  
  6973. The other function illustrated, but not yet discussed, is BIOS ROM
  6974. function 3 which returns information about the cursor. The cursor's row
  6975. is returned in the assembly language register DH, and it's column in the
  6976. assembly language register DL.
  6977.  
  6978.  
  6979.                                     
  6980.                      ADVANCED GRAPHICS ON THE IBM PC
  6981.  
  6982. This section aims to reveal more about the graphics facilities offered by
  6983. the IBM PC, in particular topics which are of a more complex nature than
  6984. those addressed in the previous section.
  6985.  
  6986.  
  6987.  
  6988. Display Pages
  6989.  
  6990. The information for display by the display card is stored in an area of
  6991. memory called the "video RAM". The size of the video RAM varies from one
  6992. display card to another, and the amount of video RAM required for a
  6993. display varies with the selected display mode. Display modes which do not
  6994. require all of the video RAM use the remaining video RAM for additional
  6995. display pages.
  6996.  
  6997.  
  6998. MODE               PAGES
  6999.  0                 8
  7000.  1                 8
  7001.  2                 4 (CGA) 8 (EGA, VGA)
  7002.  3                 4 (CGA) 8 (EGA, VGA)
  7003.  4                 1
  7004.  5                 1
  7005.  6                 1
  7006.  7                 8 (EGA, VGA)
  7007. 13                 8 (EGA, VGA)
  7008. 14                 4 (EGA, VGA)
  7009. 15                 2 (EGA, VGA)
  7010. 16                 2 (EGA, VGA)
  7011. 17                 1 (VGA)
  7012. 18                 1 (VGA)
  7013. 19                 1 (VGA)
  7014.  
  7015. Many of the BIOS ROM display functions allow selection of the display
  7016. page to be written to, regardless of which page is currently being
  7017. displayed.
  7018.  
  7019. The display card continuously updates the VDU from the information in the
  7020. active display page. Changing the active display page instantaneously
  7021. changes the display.
  7022.  
  7023. This provides the graphics programmer with the means to build a display
  7024. on an undisplayed page, and to then change the active display page so
  7025. that the viewer does not see the display being drawn.
  7026.  
  7027. Selection of the active display page is achieved by calling BIOS ROM
  7028. display function 5 with the number of the required display page stored in
  7029. the assembly language register AL;
  7030.  
  7031.           mov   ah,5
  7032.           mov   al,page
  7033.           int   10h
  7034.  
  7035. Or, from C this function becomes;
  7036.  
  7037.      #include <dos.h>
  7038.      
  7039.      void set_page(unsigned char page)
  7040.      {
  7041.           union REGS inreg, outreg;
  7042.      
  7043.           inreg.h.ah = 5;
  7044.           inreg.h.al = page;
  7045.           int86(0x10,&inreg,&outreg);
  7046.      }
  7047.      
  7048. The display page to which BIOS ROM display functions write is decided by
  7049. the value stored in the assembly language BH register. The functions for
  7050. setting a pixel's colour may be amended thus;
  7051.  
  7052.           mov   ah, 12
  7053.           mov   al, colour
  7054.           mov   bh, page
  7055.           mov   cx, x_coord
  7056.           mov   dx, y_coord
  7057.           int   10h
  7058.  
  7059. And the corresponding C function becomes;
  7060.  
  7061.      #include <dos.h>
  7062.      
  7063.      void plot(int x_coord, int y_coord, unsigned char colour, unsigned
  7064.      char page)
  7065.      {
  7066.           /* Sets the colour of a pixel */
  7067.      
  7068.           union REGS inreg, outreg;
  7069.      
  7070.           inreg.h.ah = 12;
  7071.           inreg.h.al = colour;
  7072.           inreg.h.bh = page;
  7073.           inreg.x.cx = x_coord;
  7074.           inreg.x.dx = y_coord;
  7075.           int86(0x10,&inreg,&outreg);
  7076.      }
  7077.  
  7078. The currently active display page can be determined by calling BIOS ROM
  7079. display function 15. This function returns the active display page in the
  7080. assembly language register BH;
  7081.  
  7082.           mov   ah,15
  7083.           int   10h
  7084.                               ; BH now holds active page number
  7085.  
  7086.                                     
  7087.                          Advanced Text Routines
  7088.  
  7089. When the IBM PC display is in a text mode a blinking cursor is displayed
  7090. at the current cursor position. This cursor is formed of a rectangle
  7091. which is one complete character width, but it's top and bottom pixel
  7092. lines are definable within the limits of the character height by calling
  7093. BIOS ROM display function 1. A CGA display has a character height of 8
  7094. pixel lines, an EGA display has a character height of 14 lines and a VGA
  7095. display has a character height of 16 lines.
  7096.  
  7097. BIOS ROM function 1 is called with the top pixel line number of the
  7098. desired cursor shape in assembly language register CH and the bottom
  7099. pixel line number in assembly language register CL;
  7100.  
  7101.  
  7102.           mov   ah,1
  7103.           mov   ch,top
  7104.           mov   cl,bottom
  7105.           int   10h
  7106.  
  7107. A C function to set the cursor shape may be be written thus;
  7108.  
  7109.      #include <dos.h>
  7110.      
  7111.      void setcursor(unsigned char top, unsigned char bottom)
  7112.      {
  7113.           union REGS inreg, outreg;
  7114.      
  7115.           inreg.h.ch = start;
  7116.           inreg.h.cl = end;
  7117.           inreg.h.ah = 1;
  7118.           int86(0x10, &inreg, &outreg);
  7119.      }
  7120.      
  7121. If the top pixel line is defined as larger than the bottom line, then the
  7122. cursor will appear as a pair of parallel rectangles.
  7123.  
  7124. The cursor may be removed from view by calling BIOS ROM display function
  7125. 1 with a value of 32 in the assembly language CH register.
  7126.  
  7127. The current shape of the cursor can be determined by calling BIOS ROM
  7128. function 3, which returns the top pixel line number of the cursor shape
  7129. in the assembly language CH register, and the bottom line number in the
  7130. assembly language register CL.
  7131.  
  7132. Two functions are provided by the BIOS ROM for scrolling of the currently
  7133. active display page. These are function 6, which scrolls the display up
  7134. and function 7 which scrolls the display down.
  7135.  
  7136. Both functions accept the same parameters, these being the number of
  7137. lines to scroll in the assembly language register AL, the colour
  7138. attribute for the resulting blank line in the assembly language BH
  7139. register, the top row of the area to be scrolled in the assembly language
  7140. CH register, the left column of the area to be scrolled in the assembly
  7141. language CL register, the bottom row to be scrolled in the assembly
  7142. language DH register and the right most column to be scrolled in the
  7143. assembly language DL register.
  7144.  
  7145. If the number of lines being scrolled is greater than the number of lines
  7146. in the specified area, then the result is to clear the specified area,
  7147. filling it with spaces in the attribute specified in the assembly
  7148. language BH register.
  7149.  
  7150.  
  7151. Scrolling
  7152.  
  7153. A C function to scroll the entire screen down one line can be written
  7154. thus;
  7155.  
  7156.      #include <dos.h>
  7157.      
  7158.      void scroll_down(unsigned char attr)
  7159.      {
  7160.           union REGS inreg, outreg;
  7161.      
  7162.           inreg.h.al = 1;
  7163.           inreg.h.cl = 0;
  7164.           inreg.h.ch = 0;
  7165.           inreg.h.dl = 79;
  7166.           inreg.h.dh = 24;    /* Assuming a 25 line display */
  7167.           inreg.h.bh = attr;
  7168.           inreg.h.ah = 7;
  7169.           int86(0x10, &inreg, &outreg);
  7170.      }
  7171.  
  7172.  
  7173. Clear Screen
  7174. A simple clear screen function can be written in C based upon the
  7175. "scroll_down()" function simply by changing the value assigned to
  7176. inreg.h.al to 0;
  7177.  
  7178.      #include <dos.h>
  7179.      
  7180.      void cls(unsigned char attr)
  7181.      {
  7182.           union REGS inreg, outreg;
  7183.      
  7184.           inreg.h.al = 0;
  7185.           inreg.h.cl = 0;
  7186.           inreg.h.ch = 0;
  7187.           inreg.h.dl = 79;
  7188.           inreg.h.dh = 24;    /* Assuming a 25 line display */
  7189.           inreg.h.bh = attr;
  7190.           inreg.h.ah = 7;
  7191.           int86(0x10, &inreg, &outreg);
  7192.      }
  7193.  
  7194.  
  7195.  
  7196. Windowing
  7197. Windowing functions need to preserve the display they overwrite, and
  7198. restore it when the window is removed from display. The BIOS ROM provides
  7199. a display function which enables this to be done.
  7200.  
  7201. Function 8 requires the appropriate display page number to be stored in
  7202. assembly language register BH, and then when called it returns the ascii
  7203. code of the character at the current cursor position of that display page
  7204. in the assembly language AL register, and the display attribute of the
  7205. character in the assembly language AH register.
  7206.  
  7207. The following C functions allow an area of the display to be preserved,
  7208. and later restored;
  7209.  
  7210.      #include <dos.h>
  7211.      
  7212.      void at(unsigned char row, unsigned char column, unsigned char page)
  7213.      {
  7214.           /* Position the cursor */
  7215.      
  7216.           union REGS inreg,outreg;
  7217.      
  7218.           inreg.h.ah = 2;
  7219.           inreg.h.bh = page;
  7220.           inreg.h.dh = row;
  7221.           inreg.h.dl = column;
  7222.           int86(0x10,&inreg,&outreg);
  7223.      }
  7224.      
  7225.      void get_win(unsigned char left, unsigned char top, unsigned char
  7226.      right,unsigned char bottom, unsigned char page,        char *buffer)
  7227.      {
  7228.           /* Read a text window into a variable */
  7229.      
  7230.           union REGS inreg,outreg;
  7231.      
  7232.           unsigned char old_left;
  7233.           unsigned char old_row;
  7234.           unsigned char old_col;
  7235.      
  7236.           /* save current cursor position */
  7237.           inreg.h.ah = 3;
  7238.           inreg.h.bh = page;
  7239.           int86(0x10,&inreg,&outreg);
  7240.           old_row = outreg.h.dh;
  7241.           old_col = outreg.h.dl;
  7242.      
  7243.           while(top <= bottom)
  7244.           {
  7245.                old_left = left;
  7246.                while(left <= right)
  7247.                {
  7248.                     at(top,left,page);
  7249.                     inreg.h.bh = page;
  7250.                     inreg.h.ah = 8;
  7251.                     int86(0x10,&inreg,&outreg);
  7252.                     *buffer++ = outreg.h.al;
  7253.                     *buffer++ = outreg.h.ah;
  7254.                     left++;
  7255.                }
  7256.      
  7257.                left = old_left;
  7258.                top++;
  7259.           }
  7260.      
  7261.           /* Restore cursor to original location */
  7262.           at(old_row,old_col,page);
  7263.      }
  7264.      
  7265.      void put_win(unsigned char left, unsigned char top, unsigned char
  7266.      right,  unsigned char bottom, unsigned char            page, char
  7267.      *buffer)
  7268.      {
  7269.           /* Display a text window from a variable */
  7270.      
  7271.           union REGS inreg,outreg;
  7272.      
  7273.           unsigned char old_left;
  7274.           unsigned char chr;
  7275.           unsigned char attr;
  7276.           unsigned char old_row;
  7277.           unsigned char old_col;
  7278.      
  7279.           /* save current cursor position */
  7280.           inreg.h.ah = 3;
  7281.           inreg.h.bh = page;
  7282.           int86(0x10,&inreg,&outreg);
  7283.           old_row = outreg.h.dh;
  7284.           old_col = outreg.h.dl;
  7285.      
  7286.           while(top <= bottom)
  7287.           {
  7288.                old_left = left;
  7289.                while(left <= right)
  7290.                {
  7291.                     at(top,left,page);
  7292.                     chr = *buffer++;
  7293.                     attr = *buffer++;
  7294.                     inreg.h.bh = page;
  7295.                     inreg.h.ah = 9;
  7296.                     inreg.h.al = chr;
  7297.                     inreg.h.bl = attr;
  7298.                     inreg.x.cx = 1;
  7299.                     int86(0x10,&inreg,&outreg);
  7300.                     left++;
  7301.                }
  7302.                left = old_left;
  7303.                top++;
  7304.           }
  7305.      
  7306.           /* Restore cursor to original location */
  7307.           at(old_row,old_col,page);
  7308.      }
  7309.                                     
  7310.                    DIRECT VIDEO ACCESS WITH THE IBM PC
  7311.  
  7312. Accessing video RAM directly is much faster than using the BIOS ROM
  7313. display functions. There are problems however. Different video modes
  7314. arrange their use of video RAM in different ways so a number of functions
  7315. are required for plotting using direct video access, where as only one
  7316. function is required if use is made of the BIOS ROM display function.
  7317.  
  7318. The following C function will set a pixel in CGA display modes 4 and 5
  7319. directly;
  7320.  
  7321.  
  7322.      void dplot4(int y, int x, int colour)
  7323.      {
  7324.           /* Direct plotting in modes 4 & 5 ONLY! */
  7325.      
  7326.           union mask
  7327.           {
  7328.                char c[2];
  7329.                int i;
  7330.           }bit_mask;
  7331.      
  7332.           int index;
  7333.           int bit_position;
  7334.      
  7335.           unsigned char t;
  7336.           char xor;
  7337.      
  7338.           char far *ptr = (char far *) 0xB8000000;
  7339.      
  7340.           bit_mask.i = 0xFF3F;
  7341.      
  7342.           if ( y < 0 || y > 319 || x < 0 || x > 199)
  7343.                return;
  7344.      
  7345.           xor = colour & 128;
  7346.      
  7347.           colour = colour & 127;
  7348.      
  7349.           bit_position = y % 4;
  7350.      
  7351.           colour <<= 2 * (3 - bit_position);
  7352.      
  7353.           bit_mask.i >>= 2 * bit_position;
  7354.      
  7355.           index = x * 40 + (y / 4);
  7356.      
  7357.           if (x % 2)
  7358.                index += 8152;
  7359.      
  7360.      
  7361.      
  7362.      
  7363.           if (!xor)
  7364.           {
  7365.                t = *(ptr + index) & bit_mask.c[0];
  7366.                *(ptr + index) = t | colour;
  7367.           }
  7368.           else
  7369.           {
  7370.                t = *(ptr + index) | (char)0;
  7371.                *(ptr + index) = t ^ colour;
  7372.           }
  7373.      }
  7374.      
  7375.  
  7376. Direct plotting in VGA mode 19 is very much simpler;
  7377.  
  7378.      void dplot19(int x, int y, unsigned char colour)
  7379.      {
  7380.           /* Direct plot in mode 19 ONLY */
  7381.           char far *video;
  7382.      
  7383.           video = MK_FP(0xA000,0);
  7384.           video[x + y * 320] = colour;
  7385. }
  7386.                                     
  7387.               ADVANCED GRAPHICS TECHNIQUES WITH THE IBM PC
  7388.  
  7389.  
  7390.  
  7391.  
  7392. Increasing Colours
  7393.  
  7394. The EGA display is limited displaying a maximum of 16 colours, however in
  7395. high resolution graphics modes (such as mode 16) the small physical size
  7396. of the pixels allows blending of adjacent colours to produce additional
  7397. shades.
  7398.  
  7399. If a line is drawn straight across a black background display in blue,
  7400. and then a subsequent line is drawn beneath it also in blue but only
  7401. plotting alternate pixels, the second line will appear in a darker shade
  7402. of the same colour.
  7403.  
  7404. The following C program illustrates this idea;
  7405.  
  7406.  
  7407.      #include <dos.h>
  7408.      
  7409.      union REGS inreg, outreg;
  7410.      
  7411.      void setvideo(unsigned char mode)
  7412.      {
  7413.           /* Sets the video display mode */
  7414.      
  7415.           inreg.h.al = mode;
  7416.           inreg.h.ah = 0;
  7417.           int86(0x10, &inreg, &outreg);
  7418.      }
  7419.      
  7420.      void plot(int x, int y, unsigned char colour)
  7421.      {
  7422.           /* Sets a pixel at the specified coordinates */
  7423.      
  7424.           inreg.h.al = colour;
  7425.           inreg.h.bh = 0;
  7426.           inreg.x.cx = x;
  7427.           inreg.x.dx = y;
  7428.           inreg.h.ah = 0x0C;
  7429.           int86(0x10, &inreg, &outreg);
  7430.      }
  7431.      
  7432.      void line(int a, int b, int c, int d, unsigned char colour)
  7433.      {
  7434.           /* Draws a straight line from (a,b) to (c,d) */
  7435.      
  7436.           int u;
  7437.           int v;
  7438.           int d1x;
  7439.           int d1y;
  7440.           int d2x;
  7441.           int d2y;
  7442.           int m;
  7443.           int n;
  7444.           int s;
  7445.           int i;
  7446.      
  7447.           u = c - a;
  7448.           v = d - b;
  7449.           if (u == 0)
  7450.           {
  7451.                d1x = 0;
  7452.                m = 0;
  7453.           }
  7454.           else
  7455.           {
  7456.                m = abs(u);
  7457.                if (u < 0)
  7458.                     d1x = -1;
  7459.                else
  7460.                if (u > 0)
  7461.                     d1x = 1;
  7462.           }
  7463.           if ( v == 0)
  7464.           {
  7465.                d1y = 0;
  7466.                n = 0;
  7467.           }
  7468.           else
  7469.           {
  7470.                n = abs(v);
  7471.                if (v < 0)
  7472.                     d1y = -1;
  7473.                else
  7474.                if (v > 0)
  7475.                     d1y = 1;
  7476.           }
  7477.           if (m > n)
  7478.           {
  7479.                d2x = d1x;
  7480.                d2y = 0;
  7481.           }
  7482.           else
  7483.           {
  7484.                d2x = 0;
  7485.                d2y = d1y;
  7486.                m = n;
  7487.                n = abs(u);
  7488.           }
  7489.           s = m / 2;
  7490.      
  7491.           for (i = 0; i <= m; i++)
  7492.           {
  7493.                plot(a,b,colour);
  7494.                s += n;
  7495.                if (s >= m)
  7496.                {
  7497.                     s -= m;
  7498.                     a += d1x;
  7499.                     b += d1y;
  7500.                }
  7501.                else
  7502.                {
  7503.                     a += d2x;
  7504.                     b += d2y;
  7505.                }
  7506.           }
  7507.      }
  7508.      
  7509.      void dot_line(int a, int b, int c, int d, int colour)
  7510.      {
  7511.           /* Draws a dotted straight line from (a,b) to (c,d) */
  7512.      
  7513.           int u;
  7514.           int v;
  7515.           int d1x;
  7516.           int d1y;
  7517.           int d2x;
  7518.           int d2y;
  7519.           int m;
  7520.           int n;
  7521.           int s;
  7522.           int i;
  7523.      
  7524.           u = c - a;
  7525.           v = d - b;
  7526.           if (u == 0)
  7527.           {
  7528.                d1x = 0;
  7529.                m = 0;
  7530.           }
  7531.           else
  7532.           {
  7533.                m = abs(u);
  7534.                if (u < 0)
  7535.                     d1x = -2;
  7536.                else
  7537.                if (u > 0)
  7538.                     d1x = 2;
  7539.           }
  7540.           if (v == 0)
  7541.           {
  7542.                d1y = 0;
  7543.                n = 0;
  7544.           }
  7545.           else
  7546.           {
  7547.                n = abs(v);
  7548.                if (v < 0)
  7549.                     d1y = -2;
  7550.                else
  7551.                if (v > 0)
  7552.                     d1y = 2;
  7553.           }
  7554.           if (m > n)
  7555.           {
  7556.                d2x = d1x;
  7557.                d2y = 0;
  7558.           }
  7559.           else
  7560.           {
  7561.                d2x = 0;
  7562.                d2y = d1y;
  7563.                m = n;
  7564.                n = abs(u);
  7565.           }
  7566.           s = m / 2;
  7567.      
  7568.           for (i = 0; i <= m; i+=2)
  7569.           {
  7570.                plot(a,b,colour);
  7571.                s += n;
  7572.                if (s >= m)
  7573.                {
  7574.                     s -= m;
  7575.                     a += d1x;
  7576.                     b += d1y;
  7577.                }
  7578.                else
  7579.                {
  7580.                     a += d2x;
  7581.                     b += d2y;
  7582.                }
  7583.           }
  7584.      }
  7585.      
  7586.      
  7587.      void main(void)
  7588.      {
  7589.           int n;
  7590.      
  7591.           /* Display different colour bands */
  7592.      
  7593.           setvideo(16);
  7594.      
  7595.           for(n = 1; n < 16; n++)
  7596.           {
  7597.                line(0,n * 20,639,n * 20,n);
  7598.                line(0,1 + n * 20,639,1 + n * 20,n);
  7599.                line(0,2 + n * 20,639,2 + n * 20,n);
  7600.      
  7601.                dot_line(0,4 + n * 20,639,4 + n * 20,n);
  7602.                dot_line(1,5 + n * 20,639,5 + n * 20,n);
  7603.                dot_line(0,6 + n * 20,639,6 + n * 20,n);
  7604.      
  7605.                dot_line(0,8 + n * 20,639,8 + n * 20,n);
  7606.                dot_line(1,9 + n * 20,639,9 + n * 20,n);
  7607.                dot_line(0,10 + n * 20,639,10 + n * 20,n);
  7608.      
  7609.                dot_line(1,8 + n * 20,639,8 + n * 20,7);
  7610.                dot_line(0,9 + n * 20,639,9 + n * 20,7);
  7611.                dot_line(1,10 + n * 20,639,10 + n * 20,7);
  7612.      
  7613.                dot_line(1,12 + n * 20,639,12 + n * 20,n);
  7614.                dot_line(0,13 + n * 20,639,13 + n * 20,n);
  7615.                dot_line(1,14 + n * 20,639,14 + n * 20,n);
  7616.      
  7617.                dot_line(0,12 + n * 20,639,12 + n * 20,14);
  7618.                dot_line(1,13 + n * 20,639,13 + n * 20,14);
  7619.                dot_line(0,14 + n * 20,639,14 + n * 20,14);
  7620.           }
  7621.      }
  7622.  
  7623. This technique can be put to good use for drawing three dimensional
  7624. boxes;
  7625.  
  7626.      void box3d(int xa,int ya, int xb, int yb, int col)
  7627.      {
  7628.           /* Draws a box for use in 3d histogram graphs etc */
  7629.      
  7630.           int xc;
  7631.           int xd;
  7632.      
  7633.           int n;
  7634.      
  7635.           xd = (xb - xa) / 2;
  7636.           xc = xa + xd;
  7637.      
  7638.           /* First draw the solid face */
  7639.           for(n = xa; n < xb; n++)
  7640.                line(n,ya,n,yb,col);
  7641.      
  7642.           /* Now "shaded" top and side */
  7643.           for(n = 0; n < xd; n++)
  7644.           {
  7645.                dotline(xa + n,yb - n ,xc + n,yb - n,col);
  7646.                dotline(xa + xd + n,yb - n ,xc + xd + n,yb - n,col);
  7647.                dotline(xb +n ,ya - n ,xb + n,yb - n,col);
  7648.           }
  7649.      }
  7650.      
  7651.  
  7652.  
  7653. Displaying Text At Pixel Coordinates
  7654.  
  7655. When using graphics display modes it is useful to be able to display text
  7656. not at the fixed character boundaries, but at pixel coordinates. This can
  7657. be achieved by implementing a print function which reads the character
  7658. definition data from the BIOS ROM and uses it to plot pixels to create
  7659. the shapes of the desired text. The following C function, "gr_print()"
  7660. illustrates this idea using the ROM CGA (8x8) character set;
  7661.  
  7662.      void gr_print(char *output, int x, int y, unsigned char colour)
  7663.      {
  7664.           unsigned char far *ptr;
  7665.           unsigned char chr;
  7666.           unsigned char bmask;
  7667.           int i;
  7668.           int k;
  7669.           int oldy;
  7670.           int p;
  7671.           int height;
  7672.      
  7673.           /* The height of the characters in the font being accessed */
  7674.           height = 8;
  7675.      
  7676.           /* Set pointer to start of font definition in the ROM */
  7677.           ptr = getfontptr(3);
  7678.      
  7679.           oldy = y;
  7680.           p = 0;
  7681.      
  7682.           /* Loop output string */
  7683.           while(output[p])
  7684.           {
  7685.                /* Get first character to be displayed */
  7686.                chr = output[p];
  7687.      
  7688.                /* Loop pixel lines in character definition */
  7689.                for(i = 0; i < height; i++)
  7690.                {
  7691.                     /* Get pixel line definition from the ROM */
  7692.                     bmask = *(ptr + (chr * height) + i);
  7693.      
  7694.                     /* Loop pixel columns */
  7695.                     for (k = 0; k < 8; ++k, bmask <<= 1)
  7696.                     {
  7697.                          /* Test for a set bit */
  7698.                          if(bmask & 128)
  7699.                               /* Plot a pixel if appropriate */
  7700.                               plot(x,y,colour);
  7701.                          else
  7702.                               plot(x,y,0);
  7703.                          x++;
  7704.                     }
  7705.                     /* Down to next row and left to start of character */
  7706.                     y++;
  7707.                     x -= 8;
  7708.                }
  7709.                /* Next character to be displayed */
  7710.                p++;
  7711.      
  7712.                /* Back to top row of the display position */
  7713.                y = oldy;
  7714.      
  7715.                /* Right to next character display position */
  7716.                x += 8;
  7717.           }
  7718.      }
  7719.  
  7720. The following assembly language support function is required to retrieve
  7721. the address of the ROM font by calling the BIOS ROM display function
  7722. which will return the address;
  7723.  
  7724.  
  7725.      ; GET FONT POINTER
  7726.      ; Small memory model
  7727.      ; compile with tasm /mx
  7728.      
  7729.      _TEXT     segment   byte public 'CODE'
  7730.                      assume   cs:_TEXT,ds:NOTHING
  7731.      
  7732.      _getfontptr    proc near
  7733.                push bp
  7734.                mov   bp,sp
  7735.                mov   ax,1130h
  7736.                mov   bh, [bp+4]         ; Number for font to be retrieved
  7737.                int   10h
  7738.                mov   dx,es
  7739.                mov   ax,bp
  7740.                pop   bp
  7741.                ret
  7742.      _getfontptr    endp
  7743.      
  7744.      _TEXT     ends
  7745.      
  7746.           public    _getfontptr
  7747.           end
  7748.      
  7749. The font number supplied to "getfontptr()" can be one of;
  7750.  
  7751.      2    ROM EGA 8 x 14 font
  7752.      3    ROM CGA 8 x 8 font
  7753.      6    ROM VGA 8 x 16 font
  7754.  
  7755. A Graphics Function Library For Turbo C
  7756.  
  7757.      /* Graphics library for 'Turbo C'  (V2.01) */
  7758.      
  7759.      /* (C)1992 Copyright Servile Software */
  7760.      
  7761.      #include <stdio.h>
  7762.      #include <dos.h>
  7763.      #include <stdlib.h>
  7764.      #include <stdarg.h>
  7765.      #include <string.h>
  7766.      #include <math.h>
  7767.      
  7768.      /* Global variables */
  7769.      static unsigned char attribute;
  7770.      int _dmode;
  7771.      int _lastx;
  7772.      int _lasty;
  7773.      
  7774.      /* Maximum coordinates for graphics screen */
  7775.      static int maxx;
  7776.      static int maxy;
  7777.      
  7778.      /* Sprite structure */
  7779.      struct SP
  7780.      {
  7781.           int x;
  7782.           int y;
  7783.           char data[256];
  7784.           char save[256];
  7785.      };
  7786.      typedef struct SP SPRITE;
  7787.      
  7788.      
  7789.      /* Sine and cosine tables for trig' operations */
  7790.      
  7791.      static double sintable[360] =
  7792.      {0.000000000000000001,0.017452406437283512,
  7793.                     0.034899496702500969,0.052335956242943835,
  7794.                     0.069756473744125302,0.087155742747658166,
  7795.                     0.104528463267653457,0.121869343405147462,
  7796.                     0.139173100960065438,0.156434465040230869,
  7797.                     0.173648177666930331,0.190808995376544804,
  7798.                     0.207911690817759315,0.224951054343864976,
  7799.                     0.241921895599667702,0.258819045102520739,
  7800.                     0.275637355816999163,0.292371704722736769,
  7801.                     0.309016994374947451,0.325568154457156755,
  7802.                     0.342020143325668824,0.358367949545300379,
  7803.                     0.374606593415912181,0.390731128489273882,
  7804.                     0.406736643075800375,0.422618261740699608,
  7805.                     0.438371146789077626,0.453990499739547027,
  7806.                     0.469471562785891028,0.484809620246337225,
  7807.                     0.500000000000000222,0.515038074910054489,
  7808.                     0.529919264233205234,0.544639035015027417,
  7809.                     0.559192903470747127,0.573576436351046381,
  7810.                     0.587785252292473470,0.601815023152048600,
  7811.                     0.615661475325658625,0.629320391049837835,
  7812.                     0.642787609686539696,0.656059028990507720,
  7813.                     0.669130606358858682,0.681998360062498921,
  7814.                     0.694658370458997698,0.707106781186548017,
  7815.                     0.719339800338651636,0.731353701619170904,
  7816.                     0.743144825477394688,0.754709580222772458,
  7817.                     0.766044443118978569,0.777145961456971346,
  7818.                     0.788010753606722458,0.798635510047293384,
  7819.                     0.809016994374947895,0.819152044288992243,
  7820.                     0.829037572555042179,0.838670567945424494,
  7821.                     0.848048096156426512,0.857167300702112778,
  7822.                     0.866025403784439152,0.874619707139396296,
  7823.                     0.882947592858927432,0.891006524188368343,
  7824.                     0.898794046299167482,0.906307787036650381,
  7825.                     0.913545457642601311,0.920504853452440819,
  7826.                     0.927183854566787868,0.933580426497202187,
  7827.                     0.939692620785908761,0.945518575599317179,
  7828.                     0.951056516295153975,0.956304755963035880,
  7829.                     0.961261695938319227,0.965925826289068645,
  7830.                     0.970295726275996806,0.974370064785235579,
  7831.                     0.978147600733805911,0.981627183447664198,
  7832.                     0.984807753012208353,0.987688340595137992,
  7833.                     0.990268068741570473,0.992546151641322205,
  7834.                     0.994521895368273512,0.996194698091745656,
  7835.                     0.997564050259824309,0.998629534754573944,
  7836.                     0.999390827019095762,0.999847695156391270,
  7837.                     1.000000000000000000,0.999847695156391159,
  7838.                     0.999390827019095651,0.998629534754573833,
  7839.                     0.997564050259824087,0.996194698091745323,
  7840.                     0.994521895368273179,0.992546151641321761,
  7841.                     0.990268068741570029,0.987688340595137437,
  7842.                     0.984807753012207687,0.981627183447663532,
  7843.                     0.978147600733805245,0.974370064785234802,
  7844.                     0.970295726275996029,0.965925826289067757,
  7845.                     0.961261695938318228,0.956304755963034880,
  7846.                     0.951056516295152865,0.945518575599316069,
  7847.                     0.939692620785907651,0.933580426497200966,
  7848.                     0.927183854566786536,0.920504853452439486,
  7849.                     0.913545457642599978,0.906307787036649048,
  7850.                     0.898794046299166149,0.891006524188367122,
  7851.                     0.882947592858926211,0.874619707139395186,
  7852.                     0.866025403784438042,0.857167300702111778,
  7853.                     0.848048096156425624,0.838670567945423717,
  7854.                     0.829037572555041513,0.819152044288991688,
  7855.                     0.809016994374947451,0.798635510047293051,
  7856.                     0.788010753606722236,0.777145961456971346,
  7857.                     0.766044443118978569,0.754709580222772680,
  7858.                     0.743144825477395132,0.731353701619171459,
  7859.                     0.719339800338652302,0.707106781186548794,
  7860.                     0.694658370458998808,0.681998360062500142,
  7861.                     0.669130606358860014,0.656059028990509274,
  7862.                     0.642787609686541472,0.629320391049839833,
  7863.                     0.615661475325660845,0.601815023152051043,
  7864.                     0.587785252292476135,0.573576436351049268,
  7865.                     0.559192903470750236,0.544639035015030637,
  7866.                     0.529919264233208676,0.515038074910058152,
  7867.                     0.500000000000004219,0.484809620246341444,
  7868.                     0.469471562785895413,0.453990499739551634,
  7869.                     0.438371146789082455,0.422618261740704715,
  7870.                     0.406736643075805704,0.390731128489279489,
  7871.                     0.374606593415918010,0.358367949545306430,
  7872.                     0.342020143325675152,0.325568154457163306,
  7873.                     0.309016994374954279,0.292371704722743819,
  7874.                     0.275637355817006491,0.258819045102528289,
  7875.                     0.241921895599675502,0.224951054343872997,
  7876.                     0.207911690817767558,0.190808995376553270,
  7877.                     0.173648177666939019,0.156434465040239751,
  7878.                     0.139173100960074542,0.121869343405156802,
  7879.                     0.104528463267663005,0.087155742747667922,
  7880.                     0.069756473744135267,0.052335956242954007,
  7881.                     0.034899496702511350,0.017452406437294093,
  7882.                     0.000000000000010781,-0.017452406437272534,
  7883.                     -0.034899496702489805,-0.052335956242932476,
  7884.                     -0.069756473744113756,-0.087155742747646453,
  7885.                     -0.104528463267641564,-0.121869343405135402,
  7886.                     -0.139173100960053198,-0.156434465040218462,
  7887.                     -0.173648177666917786,-0.190808995376532092,
  7888.                     -0.207911690817746464,-0.224951054343851986,
  7889.                     -0.241921895599654574,-0.258819045102507472,
  7890.                     -0.275637355816985785,-0.292371704722723225,
  7891.                     -0.309016994374933796,-0.325568154457142933,
  7892.                     -0.342020143325654891,-0.358367949545286335,
  7893.                     -0.374606593415898026,-0.390731128489259616,
  7894.                     -0.406736643075785997,-0.422618261740685175,
  7895.                     -0.438371146789063082,-0.453990499739532427,
  7896.                     -0.469471562785876373,-0.484809620246322570,
  7897.                     -0.499999999999985512,-0.515038074910039723,
  7898.                     -0.529919264233190468,-0.544639035015012540,
  7899.                     -0.559192903470732361,-0.573576436351031616,
  7900.                     -0.587785252292458593,-0.601815023152033834,
  7901.                     -0.615661475325643859,-0.629320391049823069,
  7902.                     -0.642787609686525041,-0.656059028990493065,
  7903.                     -0.669130606358844027,-0.681998360062484377,
  7904.                     -0.694658370458983265,-0.707106781186533584,
  7905.                     -0.719339800338637314,-0.731353701619156804,
  7906.                     -0.743144825477380699,-0.754709580222758580,
  7907.                     -0.766044443118964691,-0.777145961456957690,
  7908.                     -0.788010753606709025,-0.798635510047280062,
  7909.                     -0.809016994374934795,-0.819152044288979364,
  7910.                     -0.829037572555029412,-0.838670567945412060,
  7911.                     -0.848048096156414188,-0.857167300702100676,
  7912.                     -0.866025403784427272,-0.874619707139384750,
  7913.                     -0.882947592858916108,-0.891006524188357352,
  7914.                     -0.898794046299156713,-0.906307787036639945,
  7915.                     -0.913545457642591208,-0.920504853452430938,
  7916.                     -0.927183854566778320,-0.933580426497192972,
  7917.                     -0.939692620785899990,-0.945518575599308742,
  7918.                     -0.951056516295145871,-0.956304755963028108,
  7919.                     -0.961261695938311900,-0.965925826289061651,
  7920.                     -0.970295726275990256,-0.974370064785229362,
  7921.                     -0.978147600733800138,-0.981627183447658869,
  7922.                     -0.984807753012203468,-0.987688340595133552,
  7923.                     -0.990268068741566587,-0.992546151641318764,
  7924.                     -0.994521895368270514,-0.996194698091743103,
  7925.                     -0.997564050259822310,-0.998629534754572390,
  7926.                     -0.999390827019094763,-0.999847695156390714,
  7927.                     -1.000000000000000000,-0.999847695156391714,
  7928.                     -0.999390827019096761,-0.998629534754575388,
  7929.                     -0.997564050259826307,-0.996194698091748099,
  7930.                     -0.994521895368276398,-0.992546151641325647,
  7931.                     -0.990268068741574470,-0.987688340595142433,
  7932.                     -0.984807753012213349,-0.981627183447669860,
  7933.                     -0.978147600733812128,-0.974370064785242240,
  7934.                     -0.970295726276004022,-0.965925826289076417,
  7935.                     -0.961261695938327665,-0.956304755963044872,
  7936.                     -0.951056516295163523,-0.945518575599327393,
  7937.                     -0.939692620785919530,-0.933580426497213511,
  7938.                     -0.927183854566799748,-0.920504853452453253,
  7939.                     -0.913545457642614411,-0.906307787036664148,
  7940.                     -0.898794046299181804,-0.891006524188383331,
  7941.                     -0.882947592858942976,-0.874619707139412506,
  7942.                     -0.866025403784455916,-0.857167300702130208,
  7943.                     -0.848048096156444498,-0.838670567945443146,
  7944.                     -0.829037572555061497,-0.819152044289012227,
  7945.                     -0.809016994374968434,-0.798635510047314479,
  7946.                     -0.788010753606744219,-0.777145961456993772,
  7947.                     -0.766044443119001550,-0.754709580222796106,
  7948.                     -0.743144825477418891,-0.731353701619195773,
  7949.                     -0.719339800338677060,-0.707106781186574107,
  7950.                     -0.694658370459024455,-0.681998360062526232,
  7951.                     -0.669130606358886548,-0.656059028990536253,
  7952.                     -0.642787609686568784,-0.629320391049867478,
  7953.                     -0.615661475325688934,-0.601815023152079465,
  7954.                     -0.587785252292504890,-0.573576436351078467,
  7955.                     -0.559192903470779767,-0.544639035015060502,
  7956.                     -0.529919264233238985,-0.515038074910088794,
  7957.                     -0.500000000000035083,-0.484809620246372641,
  7958.                     -0.469471562785926888,-0.453990499739583386,
  7959.                     -0.438371146789114541,-0.422618261740737022,
  7960.                     -0.406736643075838289,-0.390731128489312296,
  7961.                     -0.374606593415951039,-0.358367949545339737,
  7962.                     -0.342020143325708625,-0.325568154457197001,
  7963.                     -0.309016994374988196,-0.292371704722777903,
  7964.                     -0.275637355817040797,-0.258819045102562761,
  7965.                     -0.241921895599710085,-0.224951054343907719,
  7966.                     -0.207911690817802419,-0.190808995376588242,
  7967.                     -0.173648177666974129,-0.156434465040274973,
  7968.                     -0.139173100960109847,-0.121869343405192190,
  7969.                     -0.104528463267698463,-0.087155742747703435,
  7970.                     -0.069756473744170822,-0.052335956242989604,
  7971.                     -0.034899496702546981,-0.017452406437329739
  7972.           };
  7973.  
  7974. static double costable[360] = {
  7975. 1.000000000000000000,0.999847695156391270,
  7976.                     0.999390827019095762,0.998629534754573833,
  7977.                     0.997564050259824198,0.996194698091745545,
  7978.                     0.994521895368273290,0.992546151641321983,
  7979.                     0.990268068741570362,0.987688340595137770,
  7980.                     0.984807753012208020,0.981627183447663976,
  7981.                     0.978147600733805689,0.974370064785235246,
  7982.                     0.970295726275996473,0.965925826289068312,
  7983.                     0.961261695938318894,0.956304755963035436,
  7984.                     0.951056516295153531,0.945518575599316735,
  7985.                     0.939692620785908317,0.933580426497201743,
  7986.                     0.927183854566787313,0.920504853452440264,
  7987.                     0.913545457642600867,0.906307787036649826,
  7988.                     0.898794046299166927,0.891006524188367788,
  7989.                     0.882947592858926766,0.874619707139395630,
  7990.                     0.866025403784438486,0.857167300702112112,
  7991.                     0.848048096156425846,0.838670567945423828,
  7992.                     0.829037572555041513,0.819152044288991576,
  7993.                     0.809016994374947229,0.798635510047292607,
  7994.                     0.788010753606721681,0.777145961456970569,
  7995.                     0.766044443118977680,0.754709580222771681,
  7996.                     0.743144825477393911,0.731353701619170127,
  7997.                     0.719339800338650748,0.707106781186547129,
  7998.                     0.694658370458996810,0.681998360062498032,
  7999.                     0.669130606358857682,0.656059028990506721,
  8000.                     0.642787609686538697,0.629320391049836836,
  8001.                     0.615661475325657626,0.601815023152047601,
  8002.                     0.587785252292472471,0.573576436351045382,
  8003.                     0.559192903470746128,0.544639035015026307,
  8004.                     0.529919264233204124,0.515038074910053378,
  8005.                     0.499999999999999112,0.484809620246336115,
  8006.                     0.469471562785889862,0.453990499739545861,
  8007.                     0.438371146789076460,0.422618261740698442,
  8008.                     0.406736643075799154,0.390731128489272661,
  8009.                     0.374606593415910960,0.358367949545299158,
  8010.                     0.342020143325667547,0.325568154457155479,
  8011.                     0.309016994374946230,0.292371704722735493,
  8012.                     0.275637355816997887,0.258819045102519463,
  8013.                     0.241921895599666398,0.224951054343863643,
  8014.                     0.207911690817757955,0.190808995376543389,
  8015.                     0.173648177666928888,0.156434465040229398,
  8016.                     0.139173100960063939,0.121869343405145950,
  8017.                     0.104528463267651903,0.087155742747656584,
  8018.                     0.069756473744123679,0.052335956242942190,
  8019.                     0.034899496702499304,0.017452406437281822,
  8020.                     -0.000000000000001715,-0.017452406437285253,
  8021.                     -0.034899496702502732,-0.052335956242945618,
  8022.                     -0.069756473744127107,-0.087155742747659998,
  8023.                     -0.104528463267655317,-0.121869343405149350,
  8024.                     -0.139173100960067325,-0.156434465040232784,
  8025.                     -0.173648177666932274,-0.190808995376546747,
  8026.                     -0.207911690817761285,-0.224951054343866974,
  8027.                     -0.241921895599669701,-0.258819045102522793,
  8028.                     -0.275637355817001217,-0.292371704722738768,
  8029.                     -0.309016994374949450,-0.325568154457158698,
  8030.                     -0.342020143325670822,-0.358367949545302322,
  8031.                     -0.374606593415914124,-0.390731128489275825,
  8032.                     -0.406736643075802318,-0.422618261740701329,
  8033.                     -0.438371146789079125,-0.453990499739548303,
  8034.                     -0.469471562785892083,-0.484809620246338169,
  8035.                     -0.500000000000000999,-0.515038074910054933,
  8036.                     -0.529919264233205567,-0.544639035015027528,
  8037.                     -0.559192903470747127,-0.573576436351046159,
  8038.                     -0.587785252292473026,-0.601815023152048045,
  8039.                     -0.615661475325657848,-0.629320391049836947,
  8040.                     -0.642787609686538697,-0.656059028990506499,
  8041.                     -0.669130606358857238,-0.681998360062497477,
  8042.                     -0.694658370458996033,-0.707106781186546240,
  8043.                     -0.719339800338649749,-0.731353701619168906,
  8044.                     -0.743144825477392579,-0.754709580222770238,
  8045.                     -0.766044443118976237,-0.777145961456968903,
  8046.                     -0.788010753606719905,-0.798635510047290720,
  8047.                     -0.809016994374945231,-0.819152044288989578,
  8048.                     -0.829037572555039404,-0.838670567945421719,
  8049.                     -0.848048096156423625,-0.857167300702109891,
  8050.                     -0.866025403784436265,-0.874619707139393410,
  8051.                     -0.882947592858924435,-0.891006524188365345,
  8052.                     -0.898794046299164484,-0.906307787036647494,
  8053.                     -0.913545457642598424,-0.920504853452437932,
  8054.                     -0.927183854566784982,-0.933580426497199412,
  8055.                     -0.939692620785906096,-0.945518575599314515,
  8056.                     -0.951056516295151311,-0.956304755963033326,
  8057.                     -0.961261695938316785,-0.965925826289066314,
  8058.                     -0.970295726275994586,-0.974370064785233358,
  8059.                     -0.978147600733803912,-0.981627183447662310,
  8060.                     -0.984807753012206577,-0.987688340595136327,
  8061.                     -0.990268068741569030,-0.992546151641320873,
  8062.                     -0.994521895368272291,-0.996194698091744657,
  8063.                     -0.997564050259823532,-0.998629534754573389,
  8064.                     -0.999390827019095318,-0.999847695156391048,
  8065.                     -1.000000000000000000,-0.999847695156391381,
  8066.                     -0.999390827019096095,-0.998629534754574499,
  8067.                     -0.997564050259825086,-0.996194698091746544,
  8068.                     -0.994521895368274622,-0.992546151641323537,
  8069.                     -0.990268068741572027,-0.987688340595139658,
  8070.                     -0.984807753012210241,-0.981627183447666418,
  8071.                     -0.978147600733808353,-0.974370064785238244,
  8072.                     -0.970295726275999804,-0.965925826289071865,
  8073.                     -0.961261695938322669,-0.956304755963039654,
  8074.                     -0.951056516295157972,-0.945518575599321509,
  8075.                     -0.939692620785913424,-0.933580426497207072,
  8076.                     -0.927183854566793086,-0.920504853452446370,
  8077.                     -0.913545457642607195,-0.906307787036656598,
  8078.                     -0.898794046299173921,-0.891006524188375226,
  8079.                     -0.882947592858934649,-0.874619707139403846,
  8080.                     -0.866025403784447034,-0.857167300702120993,
  8081.                     -0.848048096156435061,-0.838670567945433487,
  8082.                     -0.829037572555051505,-0.819152044289001902,
  8083.                     -0.809016994374957998,-0.798635510047303709,
  8084.                     -0.788010753606733227,-0.777145961456982559,
  8085.                     -0.766044443118990004,-0.754709580222784449,
  8086.                     -0.743144825477407012,-0.731353701619183672,
  8087.                     -0.719339800338664737,-0.707106781186561450,
  8088.                     -0.694658370459011576,-0.681998360062513242,
  8089.                     -0.669130606358873337,-0.656059028990522708,
  8090.                     -0.642787609686555128,-0.629320391049853711,
  8091.                     -0.615661475325674945,-0.601815023152065254,
  8092.                     -0.587785252292490457,-0.573576436351063812,
  8093.                     -0.559192903470765002,-0.544639035015045625,
  8094.                     -0.529919264233223886,-0.515038074910073473,
  8095.                     -0.500000000000019651,-0.484809620246357043,
  8096.                     -0.469471562785911123,-0.453990499739567510,
  8097.                     -0.438371146789098498,-0.422618261740720869,
  8098.                     -0.406736643075822024,-0.390731128489295920,
  8099.                     -0.374606593415934497,-0.358367949545323083,
  8100.                     -0.342020143325691917,-0.325568154457180181,
  8101.                     -0.309016994374971266,-0.292371704722760861,
  8102.                     -0.275637355817023644,-0.258819045102545497,
  8103.                     -0.241921895599692793,-0.224951054343890372,
  8104.                     -0.207911690817784989,-0.190808995376570756,
  8105.                     -0.173648177666956560,-0.156434465040257376,
  8106.                     -0.139173100960092194,-0.121869343405174496,
  8107.                     -0.104528463267680741,-0.087155742747685686,
  8108.                     -0.069756473744153044,-0.052335956242971805,
  8109.                     -0.034899496702529162,-0.017452406437311916,
  8110.                     -0.000000000000028605,0.017452406437254715,
  8111.                     0.034899496702471985,0.052335956242914670,
  8112.                     0.069756473744095979,0.087155742747628689,
  8113.                     0.104528463267623842,0.121869343405117708,
  8114.                     0.139173100960035545,0.156434465040200865,
  8115.                     0.173648177666900216,0.190808995376514606,
  8116.                     0.207911690817729033,0.224951054343834611,
  8117.                     0.241921895599637282,0.258819045102490264,
  8118.                     0.275637355816968632,0.292371704722706127,
  8119.                     0.309016994374916809,0.325568154457126058,
  8120.                     0.342020143325638126,0.358367949545269682,
  8121.                     0.374606593415881484,0.390731128489243240,
  8122.                     0.406736643075769733,0.422618261740669021,
  8123.                     0.438371146789047095,0.453990499739516551,
  8124.                     0.469471562785860608,0.484809620246306972,
  8125.                     0.499999999999970080,0.515038074910024402,
  8126.                     0.529919264233175369,0.544639035014997663,
  8127.                     0.559192903470717484,0.573576436351016961,
  8128.                     0.587785252292444271,0.601815023152019624,
  8129.                     0.615661475325629759,0.629320391049809191,
  8130.                     0.642787609686511385,0.656059028990479520,
  8131.                     0.669130606358830815,0.681998360062471387,
  8132.                     0.694658370458970387,0.707106781186521038,
  8133.                     0.719339800338624991,0.731353701619144592,
  8134.                     0.743144825477368709,0.754709580222746812,
  8135.                     0.766044443118953255,0.777145961456946477,
  8136.                     0.788010753606698033,0.798635510047269292,
  8137.                     0.809016994374924359,0.819152044288969150,
  8138.                     0.829037572555019531,0.838670567945402290,
  8139.                     0.848048096156404752,0.857167300702091572,
  8140.                     0.866025403784418391,0.874619707139376090,
  8141.                     0.882947592858907782,0.891006524188349247,
  8142.                     0.898794046299148941,0.906307787036632395,
  8143.                     0.913545457642583991,0.920504853452423943,
  8144.                     0.927183854566771659,0.933580426497186644,
  8145.                     0.939692620785893884,0.945518575599302968,
  8146.                     0.951056516295140320,0.956304755963022890,
  8147.                     0.961261695938306904,0.965925826289056988,
  8148.                     0.970295726275985926,0.974370064785225365,
  8149.                     0.978147600733796474,0.981627183447655538,
  8150.                     0.984807753012200360,0.987688340595130776,
  8151.                     0.990268068741564034,0.992546151641316543,
  8152.                     0.994521895368268627,0.996194698091741548,
  8153.                     0.997564050259821089,0.998629534754571502,
  8154.                     0.999390827019094097,0.999847695156390381
  8155.      
  8156.      int dtoi(double x)
  8157.      {
  8158.           /* Rounds a floating point number to an integer */
  8159.      
  8160.           int y;
  8161.      
  8162.           y = x;
  8163.           if (y < (x + 0.5))
  8164.                y++;
  8165.           return(y);
  8166.      }
  8167.      
  8168.      int getmode(int *ncols)
  8169.      {
  8170.           /* Returns current video mode and number of columns in ncols */
  8171.      
  8172.           union REGS inreg,outreg;
  8173.      
  8174.           inreg.h.ah = 0x0F;
  8175.           int86(0x10, &inreg, &outreg);
  8176.           *ncols = outreg.h.ah;
  8177.           return(outreg.h.al);
  8178.      }
  8179.      
  8180.      void setvideo(unsigned char mode)
  8181.      {
  8182.           /* Sets the video display mode and clears the screen */
  8183.           /* (modes above 127 on VGA monitors do not clear the screen) */
  8184.      
  8185.           asm mov ax , mode;
  8186.           asm int 10h;
  8187.      
  8188.           /* Set global variables */
  8189.           switch(mode)
  8190.           {
  8191.                case 0:
  8192.                case 1:
  8193.                case 2:
  8194.                case 3: break;
  8195.                case 4:
  8196.                case 9:
  8197.                case 13:
  8198.                case 19:
  8199.                case 5: maxx = 320;
  8200.                     maxy = 200;
  8201.                     break;
  8202.                case 14:
  8203.                case 10:
  8204.                case 6: maxx = 640;
  8205.                     maxy = 200;
  8206.                     break;
  8207.                case 7: maxx = 720;
  8208.                     maxy = 400;
  8209.                     break;
  8210.                case 8: maxx = 160;
  8211.                     maxy = 200;
  8212.                     break;
  8213.                case 15:
  8214.                case 16: maxx = 640;
  8215.                      maxy = 350;
  8216.                      break;
  8217.                case 17:
  8218.                case 18: maxx = 640;
  8219.                      maxy = 480;
  8220.                      break;
  8221.      
  8222.           }
  8223.           _dmode = mode;
  8224.      }
  8225.      
  8226.      void getcursor(int *row, int *col)
  8227.      {
  8228.           /* Returns the cursor position and size for the currently
  8229.      active
  8230.              video display page */
  8231.      
  8232.           union REGS inreg,outreg;
  8233.      
  8234.           inreg.h.bh = 0;
  8235.           inreg.h.ah = 0x03;
  8236.           int86(0x10, &inreg, &outreg);
  8237.           *row = outreg.h.dh;
  8238.           *col = outreg.h.dl;
  8239.      }
  8240.      
  8241.      void setcursor(char status)
  8242.      {
  8243.           /* Set cursor size, if status is 0x20 the cursor is not
  8244.      displayed */
  8245.      
  8246.           asm mov ah,1
  8247.           asm mov ch,status
  8248.           asm mov cl,7
  8249.           asm int 10h
  8250.      }
  8251.      
  8252.      void at(int row, int col)
  8253.      {
  8254.           /* Position text cursor on current screen */
  8255.      
  8256.           asm mov bh , 0;
  8257.           asm mov dh , row;
  8258.           asm mov dl , col;
  8259.           asm mov ah , 02h;
  8260.           asm int 10h;
  8261.      }
  8262.      
  8263.      void clsc(unsigned char attrib)
  8264.      {
  8265.           /* Clear display and fill with new attribute */
  8266.      
  8267.           asm mov ax , 073dh;
  8268.           asm mov bh , attrib;
  8269.           asm mov cx , 0;
  8270.           asm mov dx , 3c4fh;
  8271.           asm int 10h;
  8272.      
  8273.           /* Now move text cursor to origin (0,0) */
  8274.           asm mov bh , 0;
  8275.           asm mov dx , 0;
  8276.           asm mov ah , 02h;
  8277.           asm int 10h;
  8278.      }
  8279.      
  8280.      void clrwindow(int x1, int y1,int x2, int y2, unsigned char attrib)
  8281.      {
  8282.           /* Clear a text window */
  8283.      
  8284.           union REGS inreg,outreg;
  8285.      
  8286.           inreg.h.al = (unsigned char)(y2 - y1 + 1);
  8287.           inreg.h.bh = attrib;
  8288.           inreg.h.cl = (unsigned char)x1;
  8289.           inreg.h.ch = (unsigned char)y1;
  8290.           inreg.h.dl = (unsigned char)x2;
  8291.           inreg.h.dh = (unsigned char)y2;
  8292.           inreg.h.ah = 0x06;
  8293.           int86(0x10, &inreg, &outreg);
  8294.      }
  8295.      
  8296.      void scrollsc(void)
  8297.      {
  8298.           /* Scroll the text screen */
  8299.      
  8300.           asm mov al,01h;
  8301.           asm mov cx,0
  8302.           asm mov dx,3c4fh;
  8303.           asm mov bh,attribute;
  8304.           asm mov ah, 06h;
  8305.           asm int 10h;
  8306.      }
  8307.      
  8308.      void winscr(unsigned char left,unsigned char top, unsigned char
  8309.      right,unsigned char bottom,
  8310.                unsigned char direction, unsigned char numlines)
  8311.      {
  8312.           /* Scroll a text window */
  8313.      
  8314.           asm mov al , numlines;
  8315.           asm mov cl , left;
  8316.           asm mov ch , top;
  8317.           asm mov dl , right;
  8318.           asm mov dh , bottom;
  8319.           asm mov bh , attribute;
  8320.           asm mov ah , direction;
  8321.           asm int 10h;
  8322.      }
  8323.      
  8324.      unsigned char attr(int foregrnd, int backgrnd)
  8325.      {
  8326.           /* Convert a colour pair into a single attribute */
  8327.      
  8328.           return((char)((backgrnd << 4) + foregrnd));
  8329.      }
  8330.      
  8331.      void shadewin(unsigned char left, unsigned char top, unsigned char
  8332.      right,   unsigned char bottom)
  8333.      {
  8334.           /* Shade a text window */
  8335.      
  8336.           int row;
  8337.           int col;
  8338.           int oldrow;
  8339.           int oldcol;
  8340.      
  8341.           /* Preserve existing coords */
  8342.           getcursor(&oldrow,&oldcol);
  8343.      
  8344.           col = right + 1;
  8345.           for (row = top + 1; row <= bottom + 1; row++)
  8346.           {
  8347.                /* Move to row,col */
  8348.                asm mov bh , 0;
  8349.                asm mov dh , row;
  8350.                asm mov dl , col;
  8351.                asm mov ah , 02h;
  8352.                asm int 10h;
  8353.                /* Get character */
  8354.                asm mov ah , 0Fh;
  8355.                asm int 10h;
  8356.                asm mov ah , 08h;
  8357.                asm int 10h;
  8358.      
  8359.                /* Write in attribute 7 */
  8360.                asm mov ah , 09h;
  8361.                asm mov bl, 07h;
  8362.                asm mov cx, 01h;
  8363.                asm int 10h;
  8364.           }
  8365.           bottom++;
  8366.           for (col = left + 1; col <= right + 1; col++)
  8367.           {
  8368.                /* Move to row,col */
  8369.                asm mov bh , 0;
  8370.                asm mov dh , bottom;
  8371.                asm mov dl , col;
  8372.                asm mov ah , 02h;
  8373.                asm int 10h;
  8374.      
  8375.                /* Get character */
  8376.                asm mov ah , 0Fh;
  8377.                asm int 10h;
  8378.                asm mov ah , 08h;
  8379.                asm int 10h;
  8380.      
  8381.                /* Write in attribute 7 */
  8382.                asm mov ah , 09h;
  8383.                asm mov bl, 07h;
  8384.                asm mov cx, 01h;
  8385.                asm int 10h;
  8386.           }
  8387.      
  8388.           /* Return to original position */
  8389.           /* Move to row,col */
  8390.           asm mov bh , 0;
  8391.           asm mov dh , oldrow;
  8392.           asm mov dl , oldcol;
  8393.           asm mov ah , 02h;
  8394.           asm int 10h;
  8395.      }
  8396.      
  8397.      void bprintf(char *format, ...)
  8398.      {
  8399.           /* print text to graphics screen correctly */
  8400.      
  8401.           va_list arg_ptr;
  8402.           char output[1000];
  8403.           int c, r, n, p = 0;
  8404.           unsigned char text;
  8405.           unsigned char page;
  8406.      
  8407.           va_start(arg_ptr, format);
  8408.           vsprintf(output, format, arg_ptr);
  8409.      
  8410.           if (strcmp(output,"\r\n") == 0)
  8411.                fputs(output,stdout);
  8412.      
  8413.           else
  8414.           {
  8415.                asm mov ah , 0Fh;
  8416.                asm int 10h;
  8417.                asm mov page, bh;
  8418.      
  8419.                getmode(&n);
  8420.                getcursor(&r,&c);
  8421.      
  8422.                while (output[p])
  8423.                {
  8424.                     text = output[p++];
  8425.                     asm mov bh , page;
  8426.                     asm mov bl , attribute;
  8427.                     asm mov cx , 01h;
  8428.                     asm mov ah , 09h;
  8429.                     asm mov al , text;
  8430.                     asm int 10h;
  8431.      
  8432.                     c++;
  8433.                     if (c < (n-1))
  8434.                          at( r, c);
  8435.                     else
  8436.                     {
  8437.                          c = 0;
  8438.                          at(++r,0);
  8439.                     }
  8440.                }
  8441.           }
  8442.      }
  8443.      
  8444.      void wrtstr(char *output)
  8445.      {
  8446.           /* TTY text output. The original screen attributes remain
  8447.      unaffected */
  8448.      
  8449.           int p = 0;
  8450.           unsigned char page;
  8451.           unsigned char text;
  8452.      
  8453.           asm mov ah , 0Fh;
  8454.           asm int 10h;
  8455.           asm mov page, bh;
  8456.      
  8457.           while (output[p])
  8458.           {
  8459.                text = output[p++];
  8460.                asm mov bh , page;
  8461.                asm mov ah , 0Eh;
  8462.                asm mov al , text;
  8463.                asm int 10h;
  8464.           }
  8465.      }
  8466.      
  8467.      void getwin(int left, int top, int right, int bottom, char *buffer)
  8468.      {
  8469.           /* Read a text window into a variable */
  8470.      
  8471.           int oldleft;
  8472.           unsigned char page;
  8473.      
  8474.           asm mov ah , 0Fh;
  8475.           asm int 10h;
  8476.           asm mov page, bh;
  8477.      
  8478.           while(top <= bottom)
  8479.           {
  8480.                oldleft = left;
  8481.                while(left <= right)
  8482.                {
  8483.                     at(top,left);
  8484.                     asm mov bh , page;
  8485.                     asm mov ah , 08h;
  8486.                     asm int 10h;
  8487.                     *buffer++ = _AL;
  8488.                     *buffer++ = _AH;
  8489.                     left++;
  8490.                }
  8491.                left = oldleft;
  8492.                top++;
  8493.           }
  8494.      }
  8495.      
  8496.      void putwin(int left, int top, int right, int bottom,char *buffer)
  8497.      {
  8498.           /* Display a text window from a variable */
  8499.      
  8500.           int oldleft;
  8501.           unsigned char chr;
  8502.           unsigned char attr;
  8503.           unsigned char page;
  8504.      
  8505.           asm mov ah , 0Fh;
  8506.           asm int 10h;
  8507.           asm mov page, bh;
  8508.      
  8509.           while(top <= bottom)
  8510.           {
  8511.                oldleft = left;
  8512.                while(left <= right)
  8513.                {
  8514.                     at(top,left);
  8515.                     chr = *buffer++;
  8516.                     attr = *buffer++;
  8517.                     asm mov bh , page;
  8518.                     asm mov ah , 09h;
  8519.                     asm mov al, chr;
  8520.                     asm mov bl, attr;
  8521.                     asm mov cx,1;
  8522.                     asm int 10h;
  8523.                     left++;
  8524.                }
  8525.                left = oldleft;
  8526.                top++;
  8527.           }
  8528.      }
  8529.      
  8530.      void setpalette(unsigned char palno)
  8531.      {
  8532.           /* Sets the video palette */
  8533.      
  8534.           asm mov bh,01h;
  8535.           asm mov bl,palno;
  8536.           asm mov ah,0Bh;
  8537.           asm int 10h;
  8538.      }
  8539.      
  8540.      void setborder(unsigned char x)
  8541.      {
  8542.           /* Set border colour */
  8543.      
  8544.           asm mov bh, x;
  8545.           asm mov ax ,1001h;
  8546.           asm int 10h;
  8547.      }
  8548.      
  8549.      void setlines(unsigned char x)
  8550.      {
  8551.           /* Set text display number of lines */
  8552.      
  8553.           asm mov ah,11h;
  8554.           asm mov al,x;
  8555.           asm mov bl,0;
  8556.           asm int 10h;
  8557.      }
  8558.      
  8559.      void graphbackground(unsigned char colour)
  8560.      {
  8561.           /* Selects the background colour for a graphics mode */
  8562.      
  8563.           asm mov bh,0;
  8564.           asm mov bl,colour;
  8565.           asm mov ah, 0Bh;
  8566.           asm int 10h;
  8567.      }
  8568.      
  8569.      void plot(int x, int y, unsigned char colour)
  8570.      {
  8571.           /* Sets a pixel at the specified coordinates to the specified
  8572.      colour.
  8573.              The variables _lastx and _lasty are updated. */
  8574.      
  8575.           unsigned char far *video;
  8576.      
  8577.           _lastx = x;
  8578.           _lasty = y;
  8579.      
  8580.           if (_dmode == 19)
  8581.           {
  8582.                video = MK_FP(0xA000,0);
  8583.                video[x+y*320] = colour;
  8584.           }
  8585.           else
  8586.           {
  8587.                asm mov al , colour;
  8588.                asm mov bh , 00;
  8589.                asm mov cx , x;
  8590.                asm mov dx , y;
  8591.                asm mov ah , 0Ch;
  8592.                asm int 10h;
  8593.           }
  8594.      }
  8595.      
  8596.      int pixset(int x, int y)
  8597.      {
  8598.           /* Returns the colour of the specified pixel */
  8599.      
  8600.           asm mov cx ,x;
  8601.           asm mov dx ,y;
  8602.           asm mov ah ,0Dh;
  8603.           asm int 10h;
  8604.           return(_AL);
  8605.      }
  8606.      
  8607.      void move(int x, int y)
  8608.      {
  8609.           /* Sets the value of the variables _lastx and _lasty */
  8610.      
  8611.           _lastx = x;
  8612.           _lasty = y;
  8613.      }
  8614.      
  8615.      void line(int a, int b, int c, int d, int col)
  8616.      {
  8617.           /* Draws a straight line from (a,b) to (c,d) in colour col */
  8618.      
  8619.           int u;
  8620.           int v;
  8621.           int d1x;
  8622.           int d1y;
  8623.           int d2x;
  8624.           int d2y;
  8625.           int m;
  8626.           int n;
  8627.           int s;
  8628.           int i;
  8629.      
  8630.           if (a < 0)
  8631.                a = 0;
  8632.           else
  8633.           if (a > maxx)
  8634.                a = maxx;
  8635.      
  8636.           if (c < 0)
  8637.                c = 0;
  8638.           else
  8639.           if (c > maxx)
  8640.                c = maxx;
  8641.      
  8642.           if (b < 0)
  8643.                b = 0;
  8644.           else
  8645.           if (b > maxy)
  8646.                b = maxy;
  8647.      
  8648.           if (d < 0)
  8649.                d = 0;
  8650.           else
  8651.           if (d > maxy)
  8652.                d = maxy;
  8653.      
  8654.           u = c - a;
  8655.           v = d - b;
  8656.      
  8657.           if (u == 0)
  8658.           {
  8659.                d1x = 0;
  8660.                m = 0;
  8661.           }
  8662.           else
  8663.           {
  8664.                m = abs(u);
  8665.                if (u < 0)
  8666.                     d1x = -1;
  8667.                else
  8668.                if (u > 0)
  8669.                     d1x = 1;
  8670.           }
  8671.           if ( v == 0)
  8672.           {
  8673.                d1y = 0;
  8674.                n = 0;
  8675.           }
  8676.           else
  8677.           {
  8678.                n = abs(v);
  8679.                if (v < 0)
  8680.                     d1y = -1;
  8681.                else
  8682.                if (v > 0)
  8683.                     d1y = 1;
  8684.           }
  8685.           if (m > n)
  8686.           {
  8687.                d2x = d1x;
  8688.                d2y = 0;
  8689.           }
  8690.           else
  8691.           {
  8692.                d2x = 0;
  8693.                d2y = d1y;
  8694.                m = n;
  8695.                n = abs(u);
  8696.           }
  8697.           s = m / 2;
  8698.      
  8699.           for (i = 0; i <= m; i++)
  8700.           {
  8701.                asm mov al , col;
  8702.                asm mov bh , 0;
  8703.                asm mov ah ,0Ch;
  8704.                asm mov cx ,a;
  8705.                asm mov dx ,b;
  8706.                asm int 10h;
  8707.      
  8708.                s += n;
  8709.                if (s >= m)
  8710.                {
  8711.                     s -= m;
  8712.                     a += d1x;
  8713.                     b += d1y;
  8714.                }
  8715.                else
  8716.                {
  8717.                     a += d2x;
  8718.                     b += d2y;
  8719.                }
  8720.           }
  8721.           _lastx = a;
  8722.           _lasty = b;
  8723.      }
  8724.      
  8725.      void ellipse(int x, int y, int xrad, int yrad,double incline,int
  8726.      col)
  8727.      {
  8728.           /* Draws an ellipse */
  8729.      
  8730.           int f;
  8731.           float a;
  8732.           float b;
  8733.           float c;
  8734.           float d;
  8735.           int cols;
  8736.           double div;
  8737.      
  8738.           incline = 1 / sintable[(int)incline];
  8739.      
  8740.           if (getmode(&cols) == 6)
  8741.                div = 2.2;
  8742.           else
  8743.                div = 1.3;
  8744.      
  8745.                /* Compensate for pixel shape */
  8746.      
  8747.           a = x + xrad;
  8748.           b = y + sintable[0] * yrad + xrad/incline / div;
  8749.      
  8750.           for(f = 5; f < 360; f += 5)
  8751.           {
  8752.                c = x + costable[f] * xrad;
  8753.                d = y + sintable[f] * yrad + (costable[f] *
  8754.      xrad)/incline/div;
  8755.      
  8756.                line(a,b,c,d,col);
  8757.      
  8758.                a = c;
  8759.                b = d;
  8760.           }
  8761.           /* Ensure join */
  8762.           line(a,b,x + xrad,y + sintable[0] * yrad + xrad/incline /
  8763.      div,col);
  8764.      }
  8765.      
  8766.      void polygon(int x, int y, int rad, int col, int sides, int start)
  8767.      {
  8768.           /* Draws a regular polygon */
  8769.      
  8770.           double f;
  8771.           double div;
  8772.           double a;
  8773.           double b;
  8774.           double c;
  8775.           double d;
  8776.           double aa;
  8777.           double bb;
  8778.           int cols;
  8779.           double step;
  8780.      
  8781.           step = 360 / sides;
  8782.      
  8783.           if (getmode(&cols) == 6)
  8784.                div = 2.2;
  8785.           else
  8786.                div = 1.3;
  8787.           aa = a = x + costable[start] * rad;
  8788.           bb = b = y + sintable[start] * rad / div;
  8789.      
  8790.           for(f = start + step; f < start + 360; f += step)
  8791.           {
  8792.                c = x + costable[(int)f] * rad;
  8793.                d = y + sintable[(int)f] * rad / div;
  8794.                line(a,b,c,d,col);
  8795.                a = c;
  8796.                b = d;
  8797.           }
  8798.           line(a,b,aa,bb,col);
  8799.      }
  8800.      
  8801.      void arc(int x, int y, int rad, int start, int end,int col)
  8802.      {
  8803.           /* Draw an arc */
  8804.      
  8805.           int f;
  8806.           float a;
  8807.           float b;
  8808.           float c;
  8809.           float d;
  8810.           int cols;
  8811.           float div;
  8812.      
  8813.           if (getmode(&cols) == 6)
  8814.                div = 2.2;
  8815.           else
  8816.                div = 1.3;
  8817.           a = x + costable[start] * rad;
  8818.           b = y + sintable[start] * rad / div;
  8819.      
  8820.           for(f = start; f <= end; f ++)
  8821.           {
  8822.                c = x + costable[f] * rad;
  8823.                d = y + sintable[f] * rad / div;
  8824.                line(a,b,c,d,col);
  8825.                a = c;
  8826.                b = d;
  8827.           }
  8828.      }
  8829.      
  8830.      void segm(int x, int y, int rad, int start, int end,int col)
  8831.      {
  8832.           /* Draw a segment of a circle */
  8833.      
  8834.           int f;
  8835.           float a;
  8836.           float b;
  8837.           float c;
  8838.           float d;
  8839.           int cols;
  8840.           double div;
  8841.      
  8842.           if (getmode(&cols) == 6)
  8843.                div = 2.2;
  8844.           else
  8845.                div = 1.3;
  8846.           a = x + costable[start] * rad;
  8847.           b = y + sintable[start] * rad / div;
  8848.      
  8849.           line(x,y,a,b,col);
  8850.      
  8851.           for(f = start; f <= end ; f ++)
  8852.           {
  8853.                c = x + costable[f] * rad;
  8854.                d = y + sintable[f] * rad / div;
  8855.                line(a,b,c,d,col);
  8856.                a = c;
  8857.                b = d;
  8858.           }
  8859.           line(x,y,a,b,col);
  8860.      }
  8861.      
  8862.      void box(int xa,int ya, int xb, int yb, int col)
  8863.      {
  8864.           /* Draws a box for use in 2d histogram graphs etc */
  8865.      
  8866.           line(xa,ya,xa,yb,col);
  8867.           line(xa,yb,xb,yb,col);
  8868.           line(xb,yb,xb,ya,col);
  8869.           line(xb,ya,xa,ya,col);
  8870.      }
  8871.      
  8872.      void tri(int xa,int ya, int xb, int yb, int xc, int yc,int col)
  8873.      {
  8874.           /* Draw a triangle */
  8875.      
  8876.           line(xa,ya,xb,yb,col);
  8877.           line(xb,yb,xc,yc,col);
  8878.           line(xc,yc,xa,ya,col);
  8879.      }
  8880.      
  8881.      void fill(int x, int y, int col,int pattern)
  8882.      {
  8883.           /* Fill a boundered shape using a hatch pattern */
  8884.      
  8885.           int xa;
  8886.           int ya;
  8887.           int bn;
  8888.           int byn;
  8889.      
  8890.           int hatch[10][8] = {     255,255,255,255,255,255,255,255,
  8891.                          128,64,32,16,8,4,2,1,
  8892.                          1,2,4,8,16,32,64,128,
  8893.                          1,2,4,8,8,4,2,1,
  8894.                          238,238,238,238,238,238,238,238,
  8895.                          170,85,170,85,170,85,170,85,
  8896.                          192,96,48,24,12,6,3,1,
  8897.                          62,62,62,0,227,227,227,0,
  8898.                          129,66,36,24,24,36,66,129,
  8899.                          146,36,146,36,146,36,146,36
  8900.                     };
  8901.      
  8902.           /* Patterns for fill, each integer describes a row of dots */
  8903.      
  8904.           xa = x;
  8905.           ya = y;  /* Save Origin */
  8906.      
  8907.           if(pixset(x,y))
  8908.                return;
  8909.      
  8910.           bn = 1;
  8911.           byn = 0;
  8912.      
  8913.           do
  8914.           {
  8915.                if (hatch[pattern][byn] != 0)
  8916.                {
  8917.                     /* If blank ignore */
  8918.                     do
  8919.                     {
  8920.                          if ((bn & hatch[pattern][byn]) == bn)
  8921.                          {
  8922.                               asm mov al , col;
  8923.                               asm mov bh , 00;
  8924.                               asm mov cx , x;
  8925.                               asm mov dx , y;
  8926.                               asm mov ah , 0Ch;
  8927.                               asm int 10h;
  8928.                          }
  8929.                          x--;
  8930.                          bn <<= 1;
  8931.                          if (bn > 128)
  8932.                               bn = 1;
  8933.                     }
  8934.                     while(!pixset(x,y) && (x > -1));
  8935.      
  8936.                     x = xa + 1;
  8937.                     bn = 128;
  8938.      
  8939.                     do
  8940.                     {
  8941.                          if ((bn & hatch[pattern][byn]) == bn)
  8942.                          {
  8943.                               asm mov al , col;
  8944.                               asm mov bh , 00;
  8945.                               asm mov cx , x;
  8946.                               asm mov dx , y;
  8947.                               asm mov ah , 0Ch;
  8948.                               asm int 10h;
  8949.                          }
  8950.                          x++;
  8951.                          bn >>=1;
  8952.                          if (bn <1)
  8953.                               bn = 128;
  8954.                     }
  8955.                     while((!pixset(x,y)) && (x <= maxx));
  8956.                }
  8957.                x = xa;
  8958.                y--;
  8959.                bn = 1;
  8960.                byn++;
  8961.                if (byn > 7)
  8962.                     byn = 0;
  8963.      
  8964.           }
  8965.           while(!pixset(x,y) && ( y > -1));
  8966.      
  8967.           /* Now travel downwards */
  8968.      
  8969.           y = ya + 1;
  8970.      
  8971.           byn = 7;
  8972.           bn = 1;
  8973.           do
  8974.           {
  8975.                /* Travel left */
  8976.                if (hatch[pattern][byn] !=0)
  8977.                {
  8978.                     do
  8979.                     {
  8980.                          if ( (bn & hatch[pattern][byn]) == bn)
  8981.                          {
  8982.                               asm mov al , col;
  8983.                               asm mov bh , 00;
  8984.                               asm mov cx , x;
  8985.                               asm mov dx , y;
  8986.                               asm mov ah , 0Ch;
  8987.                               asm int 10h;
  8988.                          }
  8989.      
  8990.                          x--;
  8991.                          bn <<= 1;
  8992.                          if (bn > 128)
  8993.                               bn = 1;
  8994.                     }
  8995.                     while(!pixset(x,y) && (x > -1));
  8996.      
  8997.                     /* Back to x origin */
  8998.                     x = xa + 1 ;
  8999.                     bn = 128;
  9000.      
  9001.                     /* Travel right */
  9002.                     do
  9003.                     {
  9004.                          if ((bn & hatch[pattern][byn]) == bn)
  9005.                          {
  9006.                               asm mov al , col;
  9007.                               asm mov bh , 00;
  9008.                               asm mov cx , x;
  9009.                               asm mov dx , y;
  9010.                               asm mov ah , 0Ch;
  9011.                               asm int 10h;
  9012.                          }
  9013.                          x++;
  9014.                          bn >>=1;
  9015.                          if (bn <1)
  9016.                               bn = 128;
  9017.                     }
  9018.                     while((!pixset(x,y)) && (x <= maxx));
  9019.                }
  9020.                x = xa;
  9021.                bn = 1;
  9022.                y++;
  9023.                byn--;
  9024.                if (byn < 0)
  9025.                     byn = 7;
  9026.           }
  9027.           while((!pixset(x,y)) && (y <= maxy));
  9028.      }
  9029.      
  9030.      void invert(int xa,int ya, int xb, int yb, int col)
  9031.      {
  9032.           /* Invert a pixel window */
  9033.      
  9034.           union REGS inreg,outreg;
  9035.      
  9036.           inreg.h.al = (unsigned char)(128 | col);
  9037.           inreg.h.ah = 0x0C;
  9038.           for(inreg.x.cx = (unsigned int)xa;      inreg.x.cx <= (unsigned
  9039.      int)xb; inreg.x.cx++)
  9040.                for(inreg.x.dx = (unsigned)ya; inreg.x.dx <= (unsigned)yb;
  9041.      inreg.x.dx++)
  9042.                     int86(0x10, &inreg, &outreg);
  9043.      }
  9044.      
  9045.      void circle(int x_centre , int y_centre, int radius, int colour)
  9046.      {
  9047.           int x,y,delta;
  9048.           int startx,endx,x1,starty,endy,y1;
  9049.           int asp_ratio;
  9050.      
  9051.           if (_dmode == 6)
  9052.                asp_ratio = 22;
  9053.           else
  9054.                asp_ratio = 13;
  9055.      
  9056.           y = radius;
  9057.           delta = 3 - 2 * radius;
  9058.           for(x = 0; x < y; )
  9059.           {
  9060.                starty = y * asp_ratio / 10;
  9061.                endy = (y + 1) * asp_ratio / 10;
  9062.                startx = x * asp_ratio / 10;
  9063.                endx = (x + 1) * asp_ratio / 10;
  9064.                for(x1 = startx; x1 < endx; ++x1)
  9065.                {
  9066.                     plot(x1+x_centre,y+y_centre,colour);
  9067.                     plot(x1+x_centre,y_centre - y,colour);
  9068.                     plot(x_centre - x1,y_centre - y,colour);
  9069.                     plot(x_centre - x1,y + y_centre,colour);
  9070.                }
  9071.      
  9072.                for(y1 = starty; y1 < endy; ++y1)
  9073.                {
  9074.                     plot(y1+x_centre,x+y_centre,colour);
  9075.                     plot(y1+x_centre,y_centre - x,colour);
  9076.                     plot(x_centre - y1,y_centre - x,colour);
  9077.                     plot(x_centre - y1,x + y_centre,colour);
  9078.                }
  9079.      
  9080.                if (delta < 0)
  9081.                     delta += 4 * x + 6;
  9082.                else
  9083.                {
  9084.                     delta += 4*(x-y)+10;
  9085.                     y--;
  9086.                }
  9087.                x++;
  9088.           }
  9089.      
  9090.           if(y)
  9091.           {
  9092.                starty = y * asp_ratio / 10;
  9093.                endy = (y + 1) * asp_ratio / 10;
  9094.                startx = x * asp_ratio / 10;
  9095.                endx = (x + 1) * asp_ratio / 10;
  9096.                for(x1 = startx; x1 < endx; ++x1)
  9097.                {
  9098.                     plot(x1+x_centre,y+y_centre,colour);
  9099.                     plot(x1+x_centre,y_centre - y,colour);
  9100.                     plot(x_centre - x1,y_centre - y,colour);
  9101.                     plot(x_centre - x1,y + y_centre,colour);
  9102.                }
  9103.      
  9104.                for(y1 = starty; y1 < endy; ++y1)
  9105.                {
  9106.                     plot(y1+x_centre,x+y_centre,colour);
  9107.                     plot(y1+x_centre,y_centre - x,colour);
  9108.                     plot(x_centre - y1,y_centre - x,colour);
  9109.                     plot(x_centre - y1,x + y_centre,colour);
  9110.                }
  9111.           }
  9112.      }
  9113.      
  9114.      void draw(int x, int y, int colour)
  9115.      {
  9116.           /* Draws a line from _lastx,_lasty to x,y */
  9117.      
  9118.           line(_lastx,_lasty,x,y,colour);
  9119.      }
  9120.      
  9121.      void psprite(SPRITE *sprite,int x,int y)
  9122.      {
  9123.           int origx;
  9124.           int origy;
  9125.           int z;
  9126.           int count;
  9127.           int col;
  9128.           int pos;
  9129.           unsigned char far *video;
  9130.      
  9131.           if (_dmode == 19)
  9132.           {
  9133.                /* Super fast direct video write in mode 19 for sprites */
  9134.      
  9135.                video = MK_FP(0xA000,0);
  9136.      
  9137.                origx = x;
  9138.                origy = y;
  9139.      
  9140.                if (sprite->x != -1)
  9141.                {
  9142.                     /* This sprite has been displayed before */
  9143.                     /* replace background */
  9144.                     /* This must be done first in case the sprite
  9145.      overlaps itself */
  9146.                     x = sprite->x;
  9147.                     y = sprite->y;
  9148.                     col = 0;
  9149.                     pos = x + y * 320;
  9150.                     for(count = 0; count < 256; count++)
  9151.                     {
  9152.                          video[pos] = sprite->save[count];
  9153.                          col++;
  9154.                          if (col == 16)
  9155.                          {
  9156.                               pos += 305;
  9157.                               col = 0;
  9158.                          }
  9159.                          else
  9160.                               pos++;
  9161.                     }
  9162.                }
  9163.      
  9164.                x = origx;
  9165.                y = origy;
  9166.                col = 0;
  9167.      
  9168.                pos = x + y * 320;
  9169.      
  9170.                for(count = 0; count < 256; count++)
  9171.                {
  9172.                     sprite->save[count] = video[pos];
  9173.                     z = sprite->data[count];
  9174.                     if (z != 255)
  9175.                          video[pos] = z;
  9176.      
  9177.                     col++;
  9178.                     if (col == 16)
  9179.                     {
  9180.                          pos += 305;
  9181.                          col = 0;
  9182.                     }
  9183.                     else
  9184.                          pos++;
  9185.                }
  9186.                sprite->x = origx;
  9187.                sprite->y = origy;
  9188.      
  9189.                return;
  9190.           }
  9191.      
  9192.           origx = x;
  9193.           origy = y;
  9194.      
  9195.           if (sprite->x != -1)
  9196.           {
  9197.                /* This sprite has been displayed before */
  9198.                /* replace background */
  9199.                /* This must be done first in case the sprite overlaps
  9200.      itself */
  9201.                x = sprite->x;
  9202.                y = sprite->y;
  9203.                col = 0;
  9204.                for(count = 0; count < 256; count++)
  9205.                {
  9206.                     if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))
  9207.                     {
  9208.                          z = sprite->save[count];
  9209.                          asm mov al , z;
  9210.                          asm mov bh , 00;
  9211.                          asm mov cx , x;
  9212.                          asm mov dx , y;
  9213.                          asm mov ah , 0Ch;
  9214.                          asm int 10h;
  9215.                     }
  9216.                     col++;
  9217.                     if (col == 16)
  9218.                     {
  9219.                          y++;
  9220.                          x = sprite->x;
  9221.                          col = 0;
  9222.                     }
  9223.                     else
  9224.                          x++;
  9225.                }
  9226.           }
  9227.      
  9228.           x = origx;
  9229.           y = origy;
  9230.           col = 0;
  9231.      
  9232.           for(count = 0; count < 256; count++)
  9233.           {
  9234.                if ((x >= 0) && (y >= 0) && (x < maxx) && (y < maxy))
  9235.                {
  9236.                     asm mov cx , x;
  9237.                     asm mov dx , y;
  9238.                     asm mov ah , 0Dh;
  9239.                     asm int 10h;
  9240.                     asm mov z ,al;
  9241.                     sprite->save[count] = z;
  9242.                     z = sprite->data[count];
  9243.      
  9244.                     if (z != 255)
  9245.                     {
  9246.                          asm mov al , z;
  9247.                          asm mov bh , 0;
  9248.                          asm mov cx , x;
  9249.                          asm mov dx , y;
  9250.                          asm mov ah , 0Ch;
  9251.                          asm int 10h;
  9252.                     }
  9253.                }
  9254.                col++;
  9255.                if (col == 16)
  9256.                {
  9257.                     y++;
  9258.                     x = origx;
  9259.                     col = 0;
  9260.                }
  9261.                else
  9262.                     x++;
  9263.           }
  9264.           sprite->x = origx;
  9265.           sprite->y = origy;
  9266.           return;
  9267.      }
  9268.      
  9269.      
  9270.  
  9271. Displaying A PCX File
  9272.  
  9273. The following program is offered as a practical example of graphics with
  9274. the IBM PC. It reads a file of the `PCX' format and displays the image on
  9275. the screen.
  9276.  
  9277.      
  9278.      /* Read a PCX file and display image */
  9279.      
  9280.      #include <dos.h>
  9281.      #include <io.h>
  9282.      #include <fcntl.h>
  9283.      
  9284.      typedef struct
  9285.      {
  9286.           unsigned char man;
  9287.           unsigned char version;
  9288.           unsigned char encoding;
  9289.           unsigned char bpp;
  9290.           int xmin;
  9291.           int ymin;
  9292.           int xmax;
  9293.           int ymax;
  9294.           int hdpi;
  9295.           int vdpi;
  9296.           int colormap[24];
  9297.           char reserved;
  9298.           unsigned char planes;
  9299.           int bpl;
  9300.           int palette;
  9301.           int hss;
  9302.           int vsize;
  9303.           char pad[54];
  9304.      }
  9305.      PCX_HEADER;
  9306.      
  9307.      PCX_HEADER header;
  9308.      
  9309.      int x;
  9310.      int y;
  9311.      
  9312.      union REGS inreg,outreg;
  9313.      
  9314.      void setvideo(unsigned char mode)
  9315.      {
  9316.           /* Sets the video display mode     and clears the screen */
  9317.      
  9318.           inreg.h.al = mode;
  9319.           inreg.h.ah = 0x00;
  9320.           int86(0x10, &inreg, &outreg);
  9321.      }
  9322.      
  9323.      void plot(int x, int y, unsigned char colour)
  9324.      {
  9325.      
  9326.              if (x < header.xmax && y < header.ymax)
  9327.              {
  9328.      
  9329.              /* Direct video plot in modes 16 & 18 only! */
  9330.              asm mov   ax,y;
  9331.              asm mov   dx,80;
  9332.              asm mul   dx;
  9333.              asm mov   bx,x;
  9334.              asm mov   cl,bl;
  9335.      
  9336.              asm shr   bx,1;
  9337.              asm shr   bx,1;
  9338.              asm shr   bx,1;
  9339.              asm add   bx,ax;
  9340.      
  9341.              asm and   cl,7;
  9342.              asm xor   cl,7;
  9343.              asm mov   ah,1;
  9344.              asm shl   ah,cl;
  9345.      
  9346.              asm mov   dx,3ceh;
  9347.              asm mov   al,8;
  9348.              asm out   dx,ax;
  9349.      
  9350.              asm mov   ax,(02h shl 8) + 5;
  9351.              asm out   dx,ax;
  9352.      
  9353.              asm mov   ax,0A000h;
  9354.              asm mov   es,ax;
  9355.      
  9356.              asm mov   al,es:[bx];
  9357.              asm mov   al,byte ptr colour;
  9358.              asm mov   es:[bx],al;
  9359.      
  9360.              asm mov   ax,(0FFh shl 8 ) + 8;
  9361.              asm out   dx,ax;
  9362.      
  9363.              asm mov   ax,(00h shl 8) + 5;
  9364.              asm out   dx,ax;
  9365.           }
  9366.      }
  9367.      
  9368.      void DISPLAY(unsigned char data)
  9369.      {
  9370.           int n;
  9371.           int bit;
  9372.      
  9373.           bit = 32;
  9374.      
  9375.           for (n = 0; n < 6; n++)
  9376.           {
  9377.                if (data & bit)
  9378.                     plot(x,y,1);
  9379.                else
  9380.                     plot(x,y,0);
  9381.                bit >>= 1;
  9382.                x++;
  9383.           }
  9384.      }
  9385.      
  9386.      main(int argc, char *argv[])
  9387.      {
  9388.           int fp;
  9389.           int total_bytes;
  9390.           int n;
  9391.           unsigned char data;
  9392.           int count;
  9393.           int scan;
  9394.      
  9395.           if (argc != 2)
  9396.           {
  9397.                puts("USAGE IS getpcx <filename>");
  9398.                exit(0);
  9399.           }
  9400.      
  9401.           setvideo(16);
  9402.      
  9403.           x = 0;
  9404.           y = 0;
  9405.      
  9406.           fp = open(argv[1],O_RDONLY|O_BINARY);
  9407.      
  9408.           _read(fp,&header,128);
  9409.      
  9410.           total_bytes = header.planes * header.bpl;
  9411.      
  9412.           for(scan = 0; scan <= header.ymax; scan++)
  9413.           {
  9414.                x = 0;
  9415.      
  9416.                /* First scan line */
  9417.      
  9418.                for(n = 0; n < total_bytes; n++)
  9419.                {
  9420.                     /* Read byte */
  9421.                     _read(fp,&data,1);
  9422.      
  9423.                     count = data & 192;
  9424.      
  9425.                     if (count == 192)
  9426.                     {
  9427.                          count = data & 63;
  9428.                          n += count - 1;
  9429.                          _read(fp,&data,1);
  9430.                          while(count)
  9431.                          {
  9432.                               DISPLAY(data);
  9433.                               count--;
  9434.                          }
  9435.                     }
  9436.                     else
  9437.                          DISPLAY(data);
  9438.      
  9439.                }
  9440.                x = 0;
  9441.                y++;
  9442.           }
  9443.      }
  9444.           
  9445.  
  9446. Drawing Circles
  9447.  
  9448.  
  9449. What has drawing circles got to do with advanced C programming? Well
  9450. quite a lot, it is a task which is often desired by modern programmers,
  9451. and it is a task which can be attacked from a number of angles. This
  9452. example illustrates some of the ideas already discussed for replacing
  9453. floating point numbers with integers, and using lookup tables rather than
  9454. repeat calls to maths functions.
  9455.  
  9456. A circle may be drawn by plotting each point on its circumference. The
  9457. location of any point is given by;
  9458.  
  9459.  
  9460.      Xp = Xo + Sine(Angle) * Radius
  9461.      Yp = Yo + Cosine(Angle) * Radius
  9462.  
  9463. Where Xp,Yp is the point to be plotted, and Xo,Yo is the centre of the
  9464. circle.
  9465.  
  9466. Thus, the simplest way to draw a circle is to calculate Xp and Yp for
  9467. each angle between 1 and 360 degrees and to plot these points. There is
  9468. however one fundamental error with this. As the radius of the circle
  9469. increases, so also does the length of the arc between each angular point.
  9470. Thus a circle of sufficient radius will be drawn with a dotted line
  9471. rather than a solid line.
  9472.  
  9473. The problem of the distance between the angular points may be tackled in
  9474. two ways;
  9475.  
  9476.   1)The number of points to be plotted can be increased, to say every 30
  9477.      minutes.
  9478.   2)A straight line may be drawn between each angular point.
  9479.  
  9480. The simplest circle drawing pseudo-code may then look like this;
  9481.  
  9482.  
  9483.      FOR angle = 1 TO 360
  9484.           PLOT Xo + SINE(angle) * radius, Yo + COSINE(angle) * radius
  9485.      NEXT angle
  9486.  
  9487. This code has two major disadvantages;
  9488.  
  9489.      1) It uses REAL numbers for the sine and cosine figures
  9490.      2) It makes numerous calculations of sine and cosine values
  9491.  
  9492. Both of these disadvantages result in a very slow piece of code. Since a
  9493. circle is a regular figure with two axis of symmetry, one in both the X
  9494. and Y axis, one only needs to calculate the relative offsets of points in
  9495. one quadrant of the circle and then these offsets may be applied to the
  9496. other three quadrants to produce a faster piece of code. Faster because
  9497. the slow sine and cosine calculations are only done 90 times instead of
  9498. 360 times;
  9499.  
  9500.  
  9501.      FOR angle = 1 TO 90
  9502.           Xa = SINE(angle) * radius
  9503.           Ya = COSINE(angle) * radius
  9504.           PLOT Xo + Xa, Yo + Ya
  9505.           PLOT Xo + Xa, Yo - Ya
  9506.           PLOT Xo - Xa, Yo + Ya
  9507.           PLOT Xo - Xa, Yo - Ya
  9508.      NEXT angle
  9509.  
  9510. A further enhancement may be made by making use of sine and cosine lookup
  9511. tables instead of calculating them. This means calculating the sine and
  9512. cosine values for each required angle and storing them in a table. Then,
  9513. instead of calculating the values for each angle the circle drawing code
  9514. need only retrieve the values from a table;
  9515.  
  9516.  
  9517.      FOR angle = 1 TO 90
  9518.           Xa = SINE[angle] * radius
  9519.           Ya = COSINE[angle] * radius
  9520.           PLOT Xo + Xa, Yo + Ya
  9521.           PLOT Xo + Xa, Yo - Ya
  9522.           PLOT Xo - Xa, Yo + Ya
  9523.           PLOT Xo - Xa, Yo - Ya
  9524.      NEXT angle
  9525.  
  9526.  
  9527. Most computer languages work in RADIANS rather than DEGREES. There being
  9528. approximately 57 degrees in one radian, 2 * PI radians in one circle.
  9529. This implies that to calculate sine and cosine values of sufficient
  9530. points to draw a reasonable circle using radians one must again use real
  9531. numbers, that is numbers which have figures following a decimal point.
  9532. Real number arithmetic, also known as floating point arithmetic, is
  9533. horrendously slow to calculate. Integer arithmetic on the other hand is
  9534. very quick to calculate, but less precise.
  9535.  
  9536. To use integer arithmetic in circle drawing code requires ingenuity. If
  9537. one agrees to use sine and cosine lookup tables for degrees, rather than
  9538. radians. Then the sine value of an angle of 1 degree is;
  9539.  
  9540.        0.0175
  9541.  
  9542. Which, truncated to an integer becomes zero! To overcome this the sine
  9543. and cosine values held in the table should be multiplied by some factor,
  9544. say 10000. Then, the integer value of the sine of an angle of 1 degree
  9545. becomes;
  9546.  
  9547.        175
  9548.  
  9549. If the sine and cosine values have been multiplied by a factor, then when
  9550. the calculation of the point's offset is carried out one must remember to
  9551. divide the result by the same factor. Thus the calculation becomes;
  9552.  
  9553.           Xa = SINE[angle] * radius / factor
  9554.           Ya = COSINE[angle] * radius / factor
  9555.  
  9556. The final obstacle to drawing circles on a computer is the relationship
  9557. between the width of the display screen and its height. This ratio
  9558. between width and height is known as the "aspect ratio" and varies upon
  9559. video display mode. The IBM VGA 256 colour mode for example can display
  9560. 320 pixels across and 200 up the screen. This equates to an aspect ratio
  9561. of 1:1.3. If the circle drawing code ignores the aspect ratio, then the
  9562. shape displayed will often be ovalar to a greater or lesser degree due to
  9563. the rectangular shape of the display pixels. Thus in order to display a
  9564. true circle, the formulae to calculate each point on the circumference
  9565. must be amended to calculate a slight ellipse in compensation of the
  9566. distorting factor of the display.
  9567.  
  9568. The offset formulae then become;
  9569.  
  9570.           Xa = SINE[angle] * radius / factor
  9571.           Ya = COSINE[angle] * radius / (factor * aspect ratio)
  9572.  
  9573. The following short C program illustrates a practical circle drawing code
  9574. segment, in a demonstrable  form;
  9575.  
  9576.  
  9577.      /* Circles.c   A demonstration circle drawing program for the IBM PC
  9578.      */
  9579.      
  9580.      
  9581.      #include <stdlib.h>
  9582.      
  9583.      int sintable[91] = {0,175,349,523,698,
  9584.                872,1045,1219,1392,
  9585.                1564,1736,1908,2079,
  9586.                2250,2419,2588,2756,
  9587.                2924,3090,3256,3420,
  9588.                3584,3746,3907,4067,
  9589.                4226,4384,4540,4695,
  9590.                4848,5000,5150,5299,
  9591.                5446,5592,5736,5878,
  9592.                6018,6157,6293,6428,
  9593.                6561,6691,6820,6947,
  9594.                7071,7193,7314,7431,
  9595.                7547,7660,7771,7880,
  9596.                7986,8090,8192,8290,
  9597.                8387,8480,8572,8660,
  9598.                8746,8829,8910,8988,
  9599.                9063,9135,9205,9272,
  9600.                9336,9397,9455,9511,
  9601.                9563,9613,9659,9703,
  9602.                9744,9781,9816,9848,
  9603.                9877,9903,9925,9945,
  9604.                9962,9976,9986,9994,
  9605.                9998,10000
  9606.      };
  9607.      
  9608.      int costable[91] = { 10000,9998,9994,9986,9976,
  9609.                9962,9945,9925,9903,
  9610.                9877,9848,9816,9781,
  9611.                9744,9703,9659,9613,
  9612.                9563,9511,9455,9397,
  9613.                9336,9272,9205,9135,
  9614.                9063,8988,8910,8829,
  9615.                8746,8660,8572,8480,
  9616.                8387,8290,8192,8090,
  9617.                7986,7880,7771,7660,
  9618.                7547,7431,7314,7193,
  9619.                7071,6947,6820,6691,
  9620.                6561,6428,6293,6157,
  9621.                6018,5878,5736,5592,
  9622.                5446,5299,5150,5000,
  9623.                4848,4695,4540,4384,
  9624.                4226,4067,3907,3746,
  9625.                3584,3420,3256,3090,
  9626.                2924,2756,2588,2419,
  9627.                2250,2079,1908,1736,
  9628.                1564,1392,1219,1045,
  9629.                872,698,523,349,
  9630.                175,0
  9631.      };
  9632.      
  9633.      void setvideo(unsigned char mode)
  9634.      {
  9635.           /* Sets the video display mode for an IBM PC */
  9636.      
  9637.           asm mov al , mode;
  9638.           asm mov ah , 00;
  9639.           asm int 10h;
  9640.      }
  9641.      
  9642.      void plot(int x, int y, unsigned char colour)
  9643.      {
  9644.           /* Code for IBM PC BIOS ROM */
  9645.           /* Sets a pixel at the specified coordinates to a specified
  9646.      colour */
  9647.      
  9648.           /* Return if out of range */
  9649.           if (x < 0 || y < 0 || x > 320 || y > 200)
  9650.                return;
  9651.      
  9652.           asm mov al , colour;
  9653.           asm mov bh , 0;
  9654.           asm mov cx , x;
  9655.           asm mov dx , y;
  9656.           asm mov ah, 0Ch;
  9657.           asm int 10h;
  9658.      }
  9659.      
  9660.      void Line(int a, int b, int c, int d, int col)
  9661.      {
  9662.           /* Draws a straight line from point a,b to point c,d in colour
  9663.      col */
  9664.      
  9665.           int u;
  9666.           int v;
  9667.           int d1x;
  9668.           int d1y;
  9669.           int d2x;
  9670.           int d2y;
  9671.           int m;
  9672.           int n;
  9673.           double s; /* The only real number variable, but it's essential
  9674.      */
  9675.           int i;
  9676.      
  9677.           u = c - a;
  9678.           v = d - b;
  9679.           if (u == 0)
  9680.           {
  9681.                d1x = 0;
  9682.                m = 0;
  9683.           }
  9684.           else
  9685.           {
  9686.                m = abs(u);
  9687.                if (u < 0)
  9688.                     d1x = -1;
  9689.                else
  9690.                if (u > 0)
  9691.                     d1x = 1;
  9692.           }
  9693.      
  9694.           if ( v == 0)
  9695.           {
  9696.                d1y = 0;
  9697.                n = 0;
  9698.           }
  9699.           else
  9700.           {
  9701.                n = abs(v);
  9702.                if (v < 0)
  9703.                     d1y = -1;
  9704.                else
  9705.                if (v > 0)
  9706.                     d1y = 1;
  9707.           }
  9708.           if (m > n)
  9709.           {
  9710.                d2x = d1x;
  9711.                d2y = 0;
  9712.           }
  9713.           else
  9714.           {
  9715.                d2x = 0;
  9716.                d2y = d1y;
  9717.                m = n;
  9718.                n = abs(u);
  9719.           }
  9720.           s = m / 2;
  9721.      
  9722.           for (i = 0; i <= m; i++)
  9723.           {
  9724.                plot(a,b,col);
  9725.                s += n;
  9726.                if (s >= m)
  9727.                {
  9728.                     s -= m;
  9729.                     a += d1x;
  9730.                     b += d1y;
  9731.                }
  9732.                else
  9733.                {
  9734.                     a += d2x;
  9735.                     b += d2y;
  9736.                }
  9737.           }
  9738.      }
  9739.      
  9740.      
  9741.      void Circle(int x, int y, int rad, int col)
  9742.      {
  9743.           /* Draws a circle about origin x,y */
  9744.           /* With a radius of rad */
  9745.           /* The col parameter defines the colour for plotting */
  9746.      
  9747.           int f;
  9748.           long xa;
  9749.           long ya;
  9750.           int a1;
  9751.           int b1;
  9752.           int a2;
  9753.           int b2;
  9754.           int a3;
  9755.           int b3;
  9756.           int a4;
  9757.           int b4;
  9758.      
  9759.      
  9760.           /* Calculate first point in each segment */
  9761.      
  9762.           a1 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
  9763.           b1 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
  9764.      
  9765.           a2 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
  9766.           b2 = y + ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
  9767.      
  9768.           a3 = x - ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
  9769.           b3 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
  9770.      
  9771.           a4 = x + ((long)(costable[0]) * (long)(rad) + 5000) / 10000;
  9772.           b4 = y - ((long)(sintable[0]) * (long)(rad) + 5000) / 13000;
  9773.      
  9774.           /* Start loop at second point */
  9775.           for(f = 1; f <= 90; f++)
  9776.           {
  9777.                /* Calculate offset to new point */
  9778.                xa = ((long)(costable[f]) * (long)(rad) + 5000) / 10000;
  9779.                ya = ((long)(sintable[f]) * (long)(rad) + 5000) / 13000;
  9780.      
  9781.                /* Draw a line from the previous point to the new point in
  9782.                   each segment */
  9783.                Line(a1,b1,x + xa, y + ya,col);
  9784.                Line(a2,b2,x - xa, y + ya,col);
  9785.                Line(a3,b3,x - xa, y - ya,col);
  9786.                Line(a4,b4,x + xa, y - ya,col);
  9787.      
  9788.                /* Update the previous point in each segment */
  9789.                a1 = x + xa;
  9790.                b1 = y + ya;
  9791.                a2 = x - xa;
  9792.                b2 = y + ya;
  9793.                a3 = x - xa;
  9794.                b3 = y - ya;
  9795.                a4 = x + xa;
  9796.                b4 = y - ya;
  9797.           }
  9798.      }
  9799.      
  9800.      main()
  9801.      {
  9802.           int n;
  9803.      
  9804.           /* Select VGA 256 colour 320 x 200 video mode */
  9805.           setvideo(19);
  9806.      
  9807.           /* Draw some circles */
  9808.           for(n = 0; n < 100; n++)
  9809.                Circle(160,100,n,n + 20);
  9810.      }
  9811.      
  9812.      
  9813.  
  9814. Vesa Mode
  9815.  
  9816. The VESA BIOS provides a number of new, and exciting video modes not
  9817. supported by the standard BIOS. These modes vary from one video card to
  9818. another, but most support the following modes:
  9819.  
  9820. Mode               Display
  9821.                    
  9822. 0x54               Text 16 colours 132 x 43
  9823. 0x55               Text 16 colours 132 x 25
  9824. 0x58               Graphics 16 colours 800 x 600
  9825. 0x5C               Graphics 256 colours 800 x 600
  9826. 0x5D               Graphics 16 colours 1024 x 768
  9827. 0x5F               Graphics 256 colours 640 x 480
  9828. 0x60               Graphics 256 colours 1024 x 768
  9829. 0x64               Graphics 64k colours 640 x 480
  9830. 0x65               Graphics 64k colours 800 x 600
  9831. 0x6A               Graphics 16 colours 800 x 600
  9832. 0x6C               Graphics 16 colours 1280 x 1024
  9833. 0x70               Graphics 16m colours 320 x 200
  9834. 0x71               Graphics 16m colours 640 x 480
  9835.  
  9836. These modes are in addition to the standard BIOS video modes described
  9837. earlier.
  9838.  
  9839. Setting a VESA video mode requires a call to a different BIOS function
  9840. than the standard BIOS, as illustrated in the following example which
  9841. enables any VESA or standard display mode to be selected from the DOS
  9842. command line.
  9843.  
  9844.      #include <dos.h>
  9845.      #include <ctype.h>
  9846.      
  9847.      void setvideo(int mode)
  9848.      {
  9849.           /* Sets the video display to a VESA or normal mode and clears
  9850.      the screen */
  9851.      
  9852.           union REGS inreg,outreg;
  9853.      
  9854.           inreg.h.ah = 0x4f;
  9855.           inreg.h.al = 0x02;
  9856.           inreg.x.bx = mode;
  9857.           int86(0x10, &inreg, &outreg);
  9858.      }
  9859.      
  9860.      main(int argc, char *argv[])
  9861.      {
  9862.           setvideo(atoi(argv[1]));
  9863.      }
  9864.  
  9865.  
  9866. Plotting pixels in a VESA mode graphics display can be acgieved with the
  9867. standard BIOS plot functiona call, as illustrated here;
  9868.  
  9869.      
  9870.      void plot(int x, int y, unsigned char colour)
  9871.      {
  9872.           asm mov al , colour;
  9873.           asm mov bh , 00;
  9874.           asm mov cx , x;
  9875.           asm mov dx , y;
  9876.           asm mov ah , 0Ch;
  9877.           asm int 10h;
  9878.      }
  9879.  
  9880.  
  9881. Or, in a 800 x 600 resolution mode you can use this fast direct video
  9882. access plot function;
  9883.  
  9884.      void plot( int x, int y, unsigned char pcolor)
  9885.      {
  9886.           /*
  9887.                Fast 800 x 600 mode (0x58 or 0x102) plotting
  9888.           */
  9889.      
  9890.           asm mov   ax,y;
  9891.           asm mov   dx,800/8;
  9892.           asm mul   dx;
  9893.           asm mov   bx,x;
  9894.           asm mov   cl,bl;
  9895.      
  9896.           asm shr   bx,1;
  9897.           asm shr   bx,1;
  9898.           asm shr   bx,1;
  9899.           asm add   bx,ax;
  9900.      
  9901.           asm and   cl,7;
  9902.           asm xor   cl,7;
  9903.           asm mov   ah,1;
  9904.           asm shl   ah,cl;
  9905.      
  9906.           asm mov   dx,03CEh;
  9907.           asm mov   al,8;
  9908.           asm out   dx,ax;
  9909.      
  9910.           asm mov   ax,(02h shl 8) + 5;
  9911.           asm out   dx,ax;
  9912.      
  9913.           asm mov   ax,0A000h;
  9914.           asm mov   es,ax;
  9915.      
  9916.           asm mov   al,es:[bx];
  9917.           asm mov   al,byte ptr pcolor;
  9918.           asm mov   es:[bx],al;
  9919.      
  9920.           asm mov   ax,(0FFh shl 8 ) + 8;
  9921.           asm out   dx,ax;
  9922.      
  9923.           asm mov   ax,(00h shl 8) + 5;
  9924.           asm out   dx,ax;
  9925.      }
  9926.  
  9927. There are lots more functions supported by the VESA BIOS, but this will
  9928. get you going with the basic operations. Remember though that when using
  9929. VESA display modes, that the direct console I/O functions in the C
  9930. compiler library may not function correctly.
  9931.                                     
  9932.                DIRECTORY SEARCHING WITH THE IBM PC AND DOS
  9933.  
  9934. Amongst the many functions provided by DOS for programmers, are a pair of
  9935. functions; "Find first" and "Find next" which are used to search a DOS
  9936. directory for a specified file name or names. The first function, "Find
  9937. first" is accessed via DOS interrupt 21, function 4E. It takes an ascii
  9938. string file specification, which can include wildcards, and the required
  9939. attribute for files to match. Upon return the function fills the disk
  9940. transfer area (DTA) with details of the located file and returns with the
  9941. carry flag clear. If an error occurs, such as no matching files are
  9942. located, the function returns with the carry flag set.
  9943.  
  9944. Following a successful call to "Find first", a program can call "Find
  9945. next", DOS interrupt 21, function 4F, to locate the next file matching
  9946. the specifications provided by the initial call to "Find first". If this
  9947. function succeeds, then the DTA is filled in with details of the next
  9948. matching file, and the function returns with the carry flag clear.
  9949. Otherwise a return is made with the carry flag set.
  9950.  
  9951. Most C compilers for the IBM PC provide non standard library functions
  9952. for accessing these two functions. Turbo C provides "findfirst()" and
  9953. "findnext()". Making use of the supplied library functions shields the
  9954. programmer from the messy task of worrying about the DTA. Microsoft C
  9955. programmers should substitue findfirst() with _dos_findfirst() and
  9956. findnext() with _dos_findnext().
  9957.  
  9958. The following Turbo C example imitates the DOS directory command, in a
  9959. basic form;
  9960.  
  9961.      #include <stdio.h>
  9962.      #include <dir.h>
  9963.      #include <dos.h>
  9964.      
  9965.      void main(void)
  9966.      {
  9967.           /* Display directory listing of current directory */
  9968.      
  9969.           int done;
  9970.           int day;
  9971.           int month;
  9972.           int year;
  9973.           int hour;
  9974.           int min;
  9975.           char amflag;
  9976.           struct ffblk ffblk;
  9977.           struct fcb fcb;
  9978.      
  9979.           /* First display sub directory entries */
  9980.           done = findfirst("*.",&ffblk,16);
  9981.      
  9982.           while (!done)
  9983.           {
  9984.                year = (ffblk.ff_fdate >> 9) + 80;
  9985.                month = (ffblk.ff_fdate >> 5) & 0x0f;
  9986.                day = ffblk.ff_fdate & 0x1f;
  9987.                hour = (ffblk.ff_ftime >> 11);
  9988.                min = (ffblk.ff_ftime >> 5) & 63;
  9989.      
  9990.                amflag = 'a';
  9991.      
  9992.                if (hour > 12)
  9993.                {
  9994.                     hour -= 12;
  9995.                     amflag = 'p';
  9996.                }
  9997.      
  9998.                printf("%-11.11s  <DIR>   %02d-%02d-%02d  %2d:%02d%c\n",
  9999.                        ffblk.ff_name,day,month,year,hour,min,amflag);
  10000.                done = findnext(&ffblk);
  10001.           }
  10002.      
  10003.           /* Now all files except directories */
  10004.           done = findfirst("*.*",&ffblk,231);
  10005.      
  10006.           while (!done)
  10007.           {
  10008.                year = (ffblk.ff_fdate >> 9) + 80;
  10009.                month = (ffblk.ff_fdate >> 5) & 0x0f;
  10010.                day = ffblk.ff_fdate & 0x1f;
  10011.                hour = (ffblk.ff_ftime >> 11);
  10012.                min = (ffblk.ff_ftime >> 5) & 63;
  10013.      
  10014.                amflag = 'a';
  10015.      
  10016.                if (hour > 12)
  10017.                {
  10018.                     hour -= 12;
  10019.                     amflag = 'p';
  10020.                }
  10021.      
  10022.                parsfnm(ffblk.ff_name,&fcb,1);
  10023.      
  10024.                printf("%-8.8s %-3.3s %8ld  %02d-%02d-%02d  %2d:%02d%c\n",
  10025.                          fcb.fcb_name,fcb.fcb_ext,ffblk.ff_fsize,
  10026.                          day,month,year,hour,min,amflag);
  10027.                done = findnext(&ffblk);
  10028.           }
  10029.      }
  10030.      
  10031.  
  10032. The function "parsfnm()" is a Turbo C library command which makes use of
  10033. the DOS function for parsing an ascii string containing a file name, into
  10034. its component parts. These component parts are then put into a DOS file
  10035. control block (fcb), from where they may be easily retrieved for
  10036. displaying by printf().
  10037.  
  10038.  
  10039. The DOS DTA is comprised as follows;
  10040.  
  10041. Offset                   Length                   Contents
  10042.                                                   
  10043. 00                       15                       Reserved
  10044. 15                       Byte                     Attribute of matched
  10045.                                                   file
  10046. 16                       Word                     File time
  10047. 18                       Word                     File date
  10048. 1A                       04                       File size
  10049. 1E                       0D                       File name and extension
  10050.                                                   as ascii string
  10051.  
  10052. The file time word contains the time at which the file was last written
  10053. to disc and is comprised as follows;
  10054.  
  10055. Bits      Contents
  10056.  
  10057.  0 -  4             Seconds divided by 2
  10058.  5 - 10        Minutes
  10059. 11 - 15   Hours
  10060.  
  10061. The file date word holds the date on which the file was last written to
  10062. disc and is comprised of;
  10063.  
  10064. Bits      Contents
  10065.  
  10066.  0 -  4             Day
  10067.  5 -  8             Month
  10068.  9 - 15        Years since 1980
  10069.  
  10070. To extract these details from the DTA requires a little manipulation, as
  10071. illustrated in the above example.
  10072.  
  10073. The DTA attribute flag is comprised of the following bits being set or
  10074. not;
  10075.  
  10076. Bit       Attribute
  10077.  
  10078. 0         Read only
  10079. 1         Hidden
  10080. 2         System
  10081. 3         Volume label
  10082. 4         Directory
  10083. 5         Archive
  10084.                                     
  10085.                         ACCESSING EXPANDED MEMORY
  10086.  
  10087. Memory (RAM) in an IBM PC comes in three flavours; Conventional, Expanded
  10088. and Extended. Conventional memory is the 640K of RAM which the operating
  10089. system DOS can access. This memory is normally used. However, it is often
  10090. insufficient for todays RAM hungry systems. Expanded memory is RAM which
  10091. is addressed outside of the area of conventional RAM not by DOS but by a
  10092. second program called a LIM EMS driver. Access to this device driver is
  10093. made through interrupt 67h.
  10094.  
  10095. The main problem with accessing expanded memory is that no matter how
  10096. much expanded memory is fitted to the computer, it can only be accessed
  10097. through 16K blocks refered to as pages. So for example. If you have 2mB
  10098. of expanded RAM fitted to your PC then that is comprised of 128 pages
  10099. (128 * 16K = 2mB).
  10100.  
  10101. A program can determine whether a LIM EMS driver is installed by
  10102. attempting to open the file `EMMXXXX0' which is guarranteed by the LIM
  10103. standard to be present as an IOCTL device when the device driver is
  10104. active.
  10105.  
  10106. The following source code illustrates some basic functions for testing
  10107. for and accessing expanded memory.
  10108.  
  10109.      /*
  10110.      Various functions for using Expanded memory
  10111.      */
  10112.      
  10113.      #include <dos.h>
  10114.      #define   EMM  0x67
  10115.      
  10116.      char far *emmbase;
  10117.      emmtest()
  10118.      {
  10119.           /*
  10120.           Tests for the presence of expnaded memory by attempting to
  10121.           open the file EMMXXXX0.
  10122.           */
  10123.      
  10124.           union REGS regs;
  10125.           struct SREGS sregs;
  10126.           int error;
  10127.           long handle;
  10128.      
  10129.           /* Attempt to open the file device EMMXXXX0 */
  10130.           regs.x.ax = 0x3d00;
  10131.           regs.x.dx = (int)"EMMXXXX0";
  10132.           sregs.ds = _DS;
  10133.           intdosx(®s,®s,&sregs);
  10134.           handle = regs.x.ax;
  10135.           error = regs.x.cflag;
  10136.      
  10137.           if (!error)
  10138.           {
  10139.                regs.h.ah = 0x3e;
  10140.                regs.x.bx = handle;
  10141.                intdos(®s,®s);
  10142.           }
  10143.           return error;
  10144.      }
  10145.      
  10146.      emmok()
  10147.      {
  10148.           /*
  10149.           Checks whether the expanded memory manager responds correctly
  10150.           */
  10151.      
  10152.           union REGS regs;
  10153.      
  10154.           regs.h.ah = 0x40;
  10155.           int86(EMM,®s,®s);
  10156.      
  10157.           if (regs.h.ah)
  10158.                return 0;
  10159.      
  10160.           regs.h.ah = 0x41;
  10161.           int86(EMM,®s,®s);
  10162.      
  10163.           if (regs.h.ah)
  10164.                return 0;
  10165.      
  10166.           emmbase = MK_FP(regs.x.bx,0);
  10167.           return 1;
  10168.      }
  10169.      
  10170.      long emmavail()
  10171.      {
  10172.         /*
  10173.         Returns the number of available (free) 16K pages of expanded
  10174.      memory
  10175.         or -1 if an error occurs.
  10176.         */
  10177.      
  10178.              union REGS regs;
  10179.      
  10180.           regs.h.ah = 0x42;
  10181.           int86(EMM,®s,®s);
  10182.           if (!regs.h.ah)
  10183.                return regs.x.bx;
  10184.           return -1;
  10185.      }
  10186.      
  10187.      long emmalloc(int n)
  10188.      {
  10189.           /*
  10190.           Requests 'n' pages of expanded memory and returns the file
  10191.      handle
  10192.           assigned to the pages or -1 if there is an error
  10193.           */
  10194.      
  10195.           union REGS regs;
  10196.      
  10197.           regs.h.ah = 0x43;
  10198.           regs.x.bx = n;
  10199.           int86(EMM,®s,®s);
  10200.           if (regs.h.ah)
  10201.                return -1;
  10202.           return regs.x.dx;
  10203.      }
  10204.      
  10205.      emmmap(long handle, int phys, int page)
  10206.      {
  10207.           /*
  10208.           Maps a physical page from expanded memory into the page frame
  10209.      in the
  10210.           conventional memory 16K window so that data can be transfered
  10211.      between
  10212.           the expanded memory and conventional memory.
  10213.           */
  10214.      
  10215.           union REGS regs;
  10216.      
  10217.           regs.h.ah = 0x44;
  10218.           regs.h.al = page;
  10219.           regs.x.bx = phys;
  10220.           regs.x.dx = handle;
  10221.           int86(EMM,®s,®s);
  10222.           return (regs.h.ah == 0);
  10223.      }
  10224.      
  10225.      void emmmove(int page, char *str, int n)
  10226.      {
  10227.           /*
  10228.           Move 'n' bytes from conventional memory to the specified
  10229.      expanded memory
  10230.           page
  10231.           */
  10232.      
  10233.           char far *ptr;
  10234.      
  10235.           ptr = emmbase + page * 16384;
  10236.           while(n-- > 0)
  10237.                *ptr++ = *str++;
  10238.      }
  10239.      
  10240.      void emmget(int page, char *str, int n)
  10241.      {
  10242.           /*
  10243.           Move 'n' bytes from the specified expanded memory page into
  10244.      conventional
  10245.           memory
  10246.           */
  10247.      
  10248.           char far *ptr;
  10249.      
  10250.           ptr = emmbase + page * 16384;
  10251.           while(n-- > 0)
  10252.                *str++ = *ptr++;
  10253.      }
  10254.      
  10255.      emmclose(long handle)
  10256.      {
  10257.           /*
  10258.           Release control of the expanded memory pages allocated to
  10259.      'handle'
  10260.           */
  10261.      
  10262.           union REGS regs;
  10263.      
  10264.           regs.h.ah = 0x45;
  10265.           regs.x.dx = handle;
  10266.           int86(EMM,®s,®s);
  10267.           return (regs.h.ah == 0);
  10268.      }
  10269.      
  10270.      /*
  10271.      Test function for the EMM routines
  10272.      */
  10273.      
  10274.      void main()
  10275.      {
  10276.           long emmhandle;
  10277.           long avail;
  10278.           char teststr[80];
  10279.           int i;
  10280.      
  10281.           if(!emmtest())
  10282.           {
  10283.                printf("Expanded memory is not present\n");
  10284.                exit(0);
  10285.           }
  10286.      
  10287.           if(!emmok())
  10288.           {
  10289.                printf("Expanded memory manager is not present\n");
  10290.                exit(0);
  10291.           }
  10292.      
  10293.           avail = emmavail();
  10294.           if (avail == -1)
  10295.           {
  10296.                printf("Expanded memory manager error\n");
  10297.                exit(0);
  10298.           }
  10299.           printf("There are %ld pages available\n",avail);
  10300.      
  10301.           /* Request 10 pages of expanded memory */
  10302.           if((emmhandle = emmalloc(10)) < 0)
  10303.           {
  10304.                printf("Insufficient pages available\n");
  10305.                exit(0);
  10306.           }
  10307.      
  10308.           for (i = 0; i < 10; i++)
  10309.           {
  10310.                sprintf(teststr,"%02d This is a test string\n",i);
  10311.                emmmap(emmhandle,i,0);
  10312.                emmmove(0,teststr,strlen(teststr) + 1);
  10313.           }
  10314.      
  10315.           for (i = 0; i < 10; i++)
  10316.           {
  10317.                emmmap(emmhandle,i,0);
  10318.                emmget(0,teststr,strlen(teststr) + 1);
  10319.                printf("READING BLOCK %d: %s\n",i,teststr);
  10320.           }
  10321.      
  10322.           emmclose(emmhandle);
  10323.      }
  10324.                                     
  10325.                         ACCESSING EXTENDED MEMORY
  10326.  
  10327.  
  10328. Extended memory has all but taken over from Expanded Memory now (1996).
  10329. It is faster and more useable than expanded memory. As with Expanded
  10330. memory, Extended memory cannot be directly accessed through the standard
  10331. DOS mode, and so a transfer buffer in conventional or "real-mode" memory
  10332. needs to be used. The process to write data to Extended memory then
  10333. involves copying the data to the transfer buffer in conventional memory,
  10334. and from there copying it to Extended memory.
  10335.  
  10336. Before any use may be made of Extended memory, a program should test to
  10337. see if Extended memory is available. The following function, XMS_init(),
  10338. tests for the presence of Extended memory, and if available calls another
  10339. function, GetXMSEntry()  to initialise the program for using Extended
  10340. Memory. The function also allocates a conventional memory transfer
  10341. buffer.
  10342.  
  10343.  
  10344.      
  10345.      /*
  10346.           BLOCKSIZE will be the size of our real-memory buffer that
  10347.           we'll swap XMS through (must be a multiple of 1024, since
  10348.           XMS is allocated in 1K chunks.)
  10349.      */
  10350.      
  10351.      #ifdef __SMALL__
  10352.      #define BLOCKSIZE (16L * 1024L)
  10353.      #endif
  10354.      
  10355.      
  10356.      #ifdef __MEDIUM__
  10357.      #define BLOCKSIZE (16L * 1024L)
  10358.      #endif
  10359.      
  10360.      
  10361.      #ifdef __COMPACT__
  10362.      #define BLOCKSIZE (64L * 1024L)
  10363.      #endif
  10364.      
  10365.      #ifdef __LARGE__
  10366.      #define BLOCKSIZE (64L * 1024L)
  10367.      #endif
  10368.      
  10369.      
  10370.      char XMS_init()
  10371.      {
  10372.           /*
  10373.                returns 0 if XMS present,
  10374.                     1 if XMS absent
  10375.                     2 if unable to allocate conventional memory transfer
  10376.      buffer
  10377.           */
  10378.           unsigned char status;
  10379.           _AX=0x4300;
  10380.           geninterrupt(0x2F);
  10381.           status = _AL;
  10382.           if(status==0x80)
  10383.           {
  10384.                GetXMSEntry();
  10385.                XMSBuf = (char far *) farmalloc(BLOCKSIZE);
  10386.                if (XMSBuf == NULL)
  10387.                     return 2;
  10388.                return 0;
  10389.           }
  10390.           return 1;
  10391.      }
  10392.      
  10393.      void GetXMSEntry(void)
  10394.      {
  10395.           /*
  10396.                GetXMSEntry sets XMSFunc to the XMS Manager entry point
  10397.                so we can call it later
  10398.           */
  10399.      
  10400.           _AX=0x4310;
  10401.           geninterrupt(0x2F);
  10402.           XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);
  10403.      }
  10404.  
  10405.  
  10406. Once the presence of Extended memory has been confirmed, a program can
  10407. find out how much Extended memory is available;
  10408.  
  10409.      void XMSSize(int *kbAvail, int *largestAvail)
  10410.      {
  10411.           /*
  10412.                XMSSize returns the total kilobytes available, and the
  10413.      size
  10414.                in kilobytes of the largest available block
  10415.           */
  10416.      
  10417.           _AH=8;
  10418.           (*XMSFunc)();
  10419.           *largestAvail=_DX;
  10420.           *kbAvail=_AX;
  10421.      }
  10422.  
  10423.  
  10424. The following function may be called to allocate a block of Extended
  10425. memory, like you would allocate a block of conventional memory.
  10426.  
  10427.      char AllocXMS(unsigned long numberBytes)
  10428.      {
  10429.           /*
  10430.                Allocate a block of XMS memory numberBytes long
  10431.                Returns 1 on success
  10432.                     0 on failure
  10433.           */
  10434.      
  10435.           _DX = (int)(numberBytes / 1024);
  10436.           _AH = 9;
  10437.           (*XMSFunc)();
  10438.           if (_AX==0)
  10439.           {
  10440.                return 0;
  10441.           }
  10442.           XMSHandle=_DX;
  10443.           return 1;
  10444.      }
  10445.      
  10446.  
  10447. Allocated Extended memory is not freed by DOS. A program using Extended
  10448. memory must release it before terminating. This function frees a block of
  10449. extended memory previously allocated by AllocXMS. Note, XMSHandle is a
  10450. global variable of type int.
  10451.  
  10452.      void XMS_free(void)
  10453.      {
  10454.           /*
  10455.                Free used XMS
  10456.           */
  10457.           _DX=XMSHandle;
  10458.           _AH=0x0A;
  10459.           (*XMSFunc)();
  10460.      }
  10461.  
  10462.  
  10463. Two functions are now given. One for writing data to Extended memory, and
  10464. one for reading data from Extended memory into conventional memory.
  10465.  
  10466.      /*
  10467.           XMSParms is a structure for copying information to and from
  10468.           real-mode memory to XMS memory
  10469.      */
  10470.      
  10471.      struct parmstruct
  10472.      {
  10473.           /*
  10474.                blocklength is the size in bytes of block to copy
  10475.           */
  10476.           unsigned long blockLength;
  10477.      
  10478.           /*
  10479.                sourceHandle is the XMS handle of source; 0 means that
  10480.                sourcePtr will be a 16:16 real-mode pointer, otherwise
  10481.                sourcePtr is a 32-bit offset from the beginning of the
  10482.                XMS area that sourceHandle points to
  10483.           */
  10484.      
  10485.           unsigned int sourceHandle;
  10486.           far void *sourcePtr;
  10487.      
  10488.           /*
  10489.                destHandle is the XMS handle of destination; 0 means that
  10490.                destPtr will be a 16:16 real-mode pointer, otherwise
  10491.                destPtr is a 32-bit offset from the beginning of the XMS
  10492.                area that destHandle points to
  10493.           */
  10494.      
  10495.           unsigned int destHandle;
  10496.           far void *destPtr;
  10497.      }
  10498.      XMSParms;
  10499.      
  10500.      
  10501.      char XMS_write(unsigned long loc, char far *val, unsigned length)
  10502.      {
  10503.           /*
  10504.                Round length up to next even value
  10505.           */
  10506.           length += length % 2;
  10507.      
  10508.           XMSParms.sourceHandle=0;
  10509.           XMSParms.sourcePtr=val;
  10510.           XMSParms.destHandle=XMSHandle;
  10511.           XMSParms.destPtr=(void far *) (loc);
  10512.           XMSParms.blockLength=length;  /* Must be an even number! */
  10513.           _SI = FP_OFF(&XMSParms);
  10514.           _AH=0x0B;
  10515.           (*XMSFunc)();
  10516.           if (_AX==0)
  10517.           {
  10518.                return 0;
  10519.           }
  10520.           return 1;
  10521.      }
  10522.      
  10523.      
  10524.      void *XMS_read(unsigned long loc,unsigned length)
  10525.      {
  10526.           /*
  10527.                Returns pointer to data
  10528.                or NULL on error
  10529.           */
  10530.      
  10531.           /*
  10532.                Round length up to next even value
  10533.           */
  10534.           length += length % 2;
  10535.      
  10536.           XMSParms.sourceHandle=XMSHandle;
  10537.           XMSParms.sourcePtr=(void far *) (loc);
  10538.           XMSParms.destHandle=0;
  10539.           XMSParms.destPtr=XMSBuf;
  10540.           XMSParms.blockLength=length;       /* Must be an even number */
  10541.           _SI=FP_OFF(&XMSParms);
  10542.           _AH=0x0B;
  10543.           (*XMSFunc)();
  10544.           if (_AX==0)
  10545.           {
  10546.                return NULL;
  10547.           }
  10548.           return XMSBuf;
  10549.      }
  10550.      
  10551.      
  10552. And now putting it all together is a demonstration program.
  10553.  
  10554.      /* A sequential table of variable length records in XMS */
  10555.      
  10556.      #include <dos.h>
  10557.      #include <stdio.h>
  10558.      #include <stdlib.h>
  10559.      #include <alloc.h>
  10560.      #include <string.h>
  10561.      
  10562.      #define TRUE 1
  10563.      #define FALSE 0
  10564.      
  10565.      /*
  10566.           BLOCKSIZE will be the size of our real-memory buffer that
  10567.           we'll swap XMS through (must be a multiple of 1024, since
  10568.           XMS is allocated in 1K chunks.)
  10569.      */
  10570.      
  10571.      #ifdef __SMALL__
  10572.      #define BLOCKSIZE (16L * 1024L)
  10573.      #endif
  10574.      
  10575.      
  10576.      #ifdef __MEDIUM__
  10577.      #define BLOCKSIZE (16L * 1024L)
  10578.      #endif
  10579.      
  10580.      
  10581.      #ifdef __COMPACT__
  10582.      #define BLOCKSIZE (64L * 1024L)
  10583.      #endif
  10584.      
  10585.      #ifdef __LARGE__
  10586.      #define BLOCKSIZE (64L * 1024L)
  10587.      #endif
  10588.      
  10589.      
  10590.      /*
  10591.           XMSParms is a structure for copying information to and from
  10592.           real-mode memory to XMS memory
  10593.      */
  10594.      
  10595.      struct parmstruct
  10596.      {
  10597.           /*
  10598.                blocklength is the size in bytes of block to copy
  10599.           */
  10600.           unsigned long blockLength;
  10601.      
  10602.           /*
  10603.                sourceHandle is the XMS handle of source; 0 means that
  10604.                sourcePtr will be a 16:16 real-mode pointer, otherwise
  10605.                sourcePtr is a 32-bit offset from the beginning of the
  10606.                XMS area that sourceHandle points to
  10607.           */
  10608.      
  10609.           unsigned int sourceHandle;
  10610.           far void *sourcePtr;
  10611.      
  10612.           /*
  10613.                destHandle is the XMS handle of destination; 0 means that
  10614.                destPtr will be a 16:16 real-mode pointer, otherwise
  10615.                destPtr is a 32-bit offset from the beginning of the XMS
  10616.                area that destHandle points to
  10617.           */
  10618.      
  10619.           unsigned int destHandle;
  10620.           far void *destPtr;
  10621.      }
  10622.      XMSParms;
  10623.      
  10624.      void far (*XMSFunc) (void);   /* Used to call XMS manager
  10625.      (himem.sys) */
  10626.      char GetBuf(void);
  10627.      void GetXMSEntry(void);
  10628.      
  10629.      char *XMSBuf;  /* Conventional memory buffer for transfers */
  10630.      
  10631.      unsigned int XMSHandle;  /* handle to allocated XMS block */
  10632.      
  10633.      
  10634.      char XMS_init()
  10635.      {
  10636.           /*
  10637.                returns 0 if XMS present,
  10638.                     1 if XMS absent
  10639.                     2 if unable to allocate transfer buffer
  10640.           */
  10641.           unsigned char status;
  10642.           _AX=0x4300;
  10643.           geninterrupt(0x2F);
  10644.           status = _AL;
  10645.           if(status==0x80)
  10646.           {
  10647.                GetXMSEntry();
  10648.                XMSBuf = (char far *) farmalloc(BLOCKSIZE);
  10649.                if (XMSBuf == NULL)
  10650.                     return 2;
  10651.                return 0;
  10652.           }
  10653.           return 1;
  10654.      }
  10655.      
  10656.      void GetXMSEntry(void)
  10657.      {
  10658.           /*
  10659.                GetXMSEntry sets XMSFunc to the XMS Manager entry point
  10660.                so we can call it later
  10661.           */
  10662.      
  10663.           _AX=0x4310;
  10664.           geninterrupt(0x2F);
  10665.           XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);
  10666.      }
  10667.      
  10668.      
  10669.      void XMSSize(int *kbAvail, int *largestAvail)
  10670.      {
  10671.           /*
  10672.                XMSSize returns the total kilobytes available, and the
  10673.      size
  10674.                in kilobytes of the largest available block
  10675.           */
  10676.      
  10677.           _AH=8;
  10678.           (*XMSFunc)();
  10679.           *largestAvail=_DX;
  10680.           *kbAvail=_AX;
  10681.      }
  10682.      
  10683.      char AllocXMS(unsigned long numberBytes)
  10684.      {
  10685.           /*
  10686.                Allocate a block of XMS memory numberBytes long
  10687.           */
  10688.      
  10689.           _DX = (int)(numberBytes / 1024);
  10690.           _AH = 9;
  10691.           (*XMSFunc)();
  10692.           if (_AX==0)
  10693.           {
  10694.                return FALSE;
  10695.           }
  10696.           XMSHandle=_DX;
  10697.           return TRUE;
  10698.      }
  10699.      
  10700.      void XMS_free(void)
  10701.      {
  10702.           /*
  10703.                Free used XMS
  10704.           */
  10705.           _DX=XMSHandle;
  10706.           _AH=0x0A;
  10707.           (*XMSFunc)();
  10708.      }
  10709.      
  10710.      char XMS_write(unsigned long loc, char far *val, unsigned length)
  10711.      {
  10712.           /*
  10713.                Round length up to next even value
  10714.           */
  10715.           length += length % 2;
  10716.      
  10717.           XMSParms.sourceHandle=0;
  10718.           XMSParms.sourcePtr=val;
  10719.           XMSParms.destHandle=XMSHandle;
  10720.           XMSParms.destPtr=(void far *) (loc);
  10721.           XMSParms.blockLength=length;  /* Must be an even number! */
  10722.           _SI = FP_OFF(&XMSParms);
  10723.           _AH=0x0B;
  10724.           (*XMSFunc)();
  10725.           if (_AX==0)
  10726.           {
  10727.                return FALSE;
  10728.           }
  10729.           return TRUE;
  10730.      }
  10731.      
  10732.      
  10733.      void *XMS_read(unsigned long loc,unsigned length)
  10734.      {
  10735.           /*
  10736.                Returns pointer to data
  10737.                or NULL on error
  10738.           */
  10739.      
  10740.           /*
  10741.                Round length up to next even value
  10742.           */
  10743.           length += length % 2;
  10744.      
  10745.           XMSParms.sourceHandle=XMSHandle;
  10746.           XMSParms.sourcePtr=(void far *) (loc);
  10747.           XMSParms.destHandle=0;
  10748.           XMSParms.destPtr=XMSBuf;
  10749.           XMSParms.blockLength=length;  /* Must be an even number */
  10750.           _SI=FP_OFF(&XMSParms);
  10751.           _AH=0x0B;
  10752.           (*XMSFunc)();
  10753.           if (_AX==0)
  10754.           {
  10755.                return NULL;
  10756.           }
  10757.           return XMSBuf;
  10758.      }
  10759.      
  10760.      
  10761.      /*
  10762.           Demonstration code
  10763.           Read various length strings into a single XMS block (EMB)
  10764.           and write them out again
  10765.      */
  10766.      
  10767.      int main()
  10768.      {
  10769.           int kbAvail,largestAvail;
  10770.           char buffer[80];
  10771.           char *p;
  10772.           long pos;
  10773.           long end;
  10774.      
  10775.           if (XMS_init() == 0)
  10776.                printf("XMS Available ...\n");
  10777.           else
  10778.           {
  10779.                printf("XMS Not Available\n");
  10780.                return(1);
  10781.           }
  10782.      
  10783.           XMSSize(&kbAvail,&largestAvail);
  10784.           printf("Kilobytes Available: %d; Largest block:
  10785.      %dK\n",kbAvail,largestAvail);
  10786.      
  10787.           if (!AllocXMS(2000 * 1024L))
  10788.                return(1);
  10789.      
  10790.      
  10791.           pos = 0;
  10792.      
  10793.           do
  10794.           {
  10795.                p = fgets(buffer,1000,stdin);
  10796.                if (p != NULL)
  10797.                {
  10798.                     XMS_write(pos,buffer,strlen(buffer) + 1);
  10799.                     pos += strlen(buffer) + 1;
  10800.                }
  10801.           }
  10802.           while(p != NULL);
  10803.      
  10804.           end = pos;
  10805.      
  10806.           pos = 0;
  10807.      
  10808.           do
  10809.           {
  10810.                memcpy(buffer,XMS_read(pos,100),70);
  10811.                printf("%s",buffer);
  10812.                pos += strlen(buffer) + 1;
  10813.           }
  10814.           while(pos < end);
  10815.      
  10816.           /*
  10817.                It is VERY important to free any XMS before exiting!
  10818.           */
  10819.           XMS_free();
  10820.           return 0;
  10821.      }
  10822.  
  10823.                                     
  10824.                                     
  10825.                                     
  10826.                              TSR PROGRAMMING
  10827.  
  10828.  
  10829. Programs which remain running and resident in memory while other programs
  10830. are running are the most exciting line of programming for many PC
  10831. developers. This type of program is known as a "Terminate and Stay
  10832. Resident" or "TSR" program and they are very difficult to program
  10833. sucessfuly.
  10834.  
  10835. The difficulties in programming TSRs comes from the limitations of DOS
  10836. which is not a multi-tasking operating system, and does not react well to
  10837. re-enterant code. That is it's own functions (interrupts) calling
  10838. themselves.
  10839.  
  10840. In theory a TSR is quite simple. It is an ordinary program which
  10841. terminates not through the usual DOS terminate function, but through the
  10842. DOS "keep" function - interrupt 27h. This function reserves an area of
  10843. memory, used by the program so that no other programs will overwrite it.
  10844. This in itself is not a very difficult task, excepting that the program
  10845. needs to tell DOS how much memory to leave it!
  10846.  
  10847. The problems stem mainly from not being able to use DOS function calls
  10848. within the TSR program once it has "gone resident".
  10849.  
  10850. There are a few basic rules which help to clarify the problems
  10851. encountered in programming TSRs:
  10852.  
  10853.   1.Avoid DOS function calls
  10854.   2.Monitor the DOS busy flag, when this flag is nonzero, DOS is
  10855.      executing an interrupt 21h function and MUST NOT be disturbed!
  10856.   3.Monitor interrupt 28h. This reveals when DOS is busy waiting for
  10857.      console input. At this time you can disturb DOS regardless of the
  10858.      DOS busy flag setting.
  10859.   4.Provide some way of checking whether the TSR is already loaded to
  10860.      prevent multiple copies occuring in memory.
  10861.   5.Remember that other TSR programs may be chained to interrupts, and
  10862.      so you must chain any interrupt vectors that your program needs.
  10863.   6.Your TSR program must use its own stack, and NOT that of the running
  10864.      process.
  10865.   7.TSR programs must be compiled in a small memory model with stack
  10866.      checking turned off.
  10867.   8.When control passes to your TSR program, it must tell DOS that the
  10868.      active process has changed.
  10869.  
  10870.  
  10871. The following three source code modules describe a complete TSR program.
  10872. This is a useful pop-up address book database which can be activated
  10873. while any other program is running by pressing the key combination `Alt'
  10874. and `.'.  If the address book does not respond to the key press, it is
  10875. probably because DOS cannot be disturbed, and you should try to pop-it-up
  10876. again.
  10877.  
  10878.  
  10879.      /*
  10880.         A practical TSR program (a pop-up address book database)
  10881.         Compile in small memory model with stack checking OFF
  10882.      */
  10883.      
  10884.      #include <dos.h>
  10885.      #include <stdio.h>
  10886.      #include <string.h>
  10887.      #include <dir.h>
  10888.      
  10889.      static union REGS rg;
  10890.      
  10891.      /*
  10892.         Size of the program to remain resident
  10893.         experimentation is required to make this as small as possible
  10894.      */
  10895.      unsigned sizeprogram = 28000/16;
  10896.      
  10897.      /* Activate with Alt . */
  10898.      unsigned scancode = 52;  /* . */
  10899.      unsigned keymask = 8;         /* ALT */
  10900.      
  10901.      char signature[]= "POPADDR";
  10902.      char fpath[40];
  10903.      
  10904.      /*
  10905.           Function prototypes
  10906.      */
  10907.      
  10908.      void curr_cursor(int *x, int *y);
  10909.      int resident(char *, void interrupt(*)());
  10910.      void resinit(void);
  10911.      void terminate(void);
  10912.      void restart(void);
  10913.      void wait(void);
  10914.      void resident_psp(void);
  10915.      void exec(void);
  10916.      
  10917.      /*
  10918.         Entry point from DOS
  10919.      */
  10920.      
  10921.      main(int argc, char *argv[])
  10922.      {
  10923.           void interrupt ifunc();
  10924.           int ivec;
  10925.      
  10926.           /*
  10927.              For simplicity, assume the data file is in the root
  10928.      directory
  10929.              of drive C:
  10930.           */
  10931.           strcpy(fpath,"C:\\ADDRESS.DAT");
  10932.      
  10933.           if ((ivec = resident(signature,ifunc)) != 0)
  10934.           {
  10935.                /* TSR is resident */
  10936.                if (argc > 1)
  10937.                {
  10938.                     rg.x.ax = 0;
  10939.                     if (strcmp(argv[1],"quit") == 0)
  10940.                          rg.x.ax = 1;
  10941.                     else if (strcmp(argv[1],"restart") == 0)
  10942.                          rg.x.ax = 2;
  10943.                     else if (strcmp(argv[1],"wait") == 0)
  10944.                          rg.x.ax = 3;
  10945.                     if (rg.x.ax)
  10946.                     {
  10947.                          int86(ivec,&rg,&rg);
  10948.                          return;
  10949.                     }
  10950.                }
  10951.                printf("\nPopup Address Book is already resident");
  10952.           }
  10953.           else
  10954.           {
  10955.                /* Initial load of TSR program */
  10956.                printf("Popup Address Book Resident.\nPress Alt . To
  10957.      Activate....\n");
  10958.                resinit();
  10959.           }
  10960.      }
  10961.      
  10962.      void interrupt ifunc(bp,di,si,ds,es,dx,cx,bx,ax)
  10963.      {
  10964.           if(ax == 1)
  10965.                terminate();
  10966.           else if(ax == 2)
  10967.                restart();
  10968.           else if(ax == 3)
  10969.                wait();
  10970.      }
  10971.      
  10972.      popup()
  10973.      {
  10974.           int x,y;
  10975.      
  10976.           curr_cursor(&x,&y);
  10977.      
  10978.           /* Call the TSR C program here */
  10979.           exec();
  10980.           cursor(x,y);
  10981.      }
  10982.      
  10983.      /*
  10984.           Second source module
  10985.      */
  10986.      
  10987.      #include <dos.h>
  10988.      #include <stdio.h>
  10989.      
  10990.      static union REGS rg;
  10991.      static struct SREGS seg;
  10992.      static unsigned mcbseg;
  10993.      static unsigned dosseg;
  10994.      static unsigned dosbusy;
  10995.      static unsigned enddos;
  10996.      char far *intdta;
  10997.      static unsigned intsp;
  10998.      static unsigned intss;
  10999.      static char far *mydta;
  11000.      static unsigned myss;
  11001.      static unsigned stack;
  11002.      static unsigned ctrl_break;
  11003.      static unsigned mypsp;
  11004.      static unsigned intpsp;
  11005.      static unsigned pids[2];
  11006.      static int pidctr = 0;
  11007.      static int pp;
  11008.      static void interrupt (*oldtimer)();
  11009.      static void interrupt (*old28)();
  11010.      static void interrupt (*oldkb)();
  11011.      static void interrupt (*olddisk)();
  11012.      static void interrupt (*oldcrit)();
  11013.      
  11014.      void interrupt newtimer();
  11015.      void interrupt new28();
  11016.      void interrupt newkb();
  11017.      void interrupt newdisk();
  11018.      void interrupt newcrit();
  11019.      
  11020.      extern unsigned sizeprogram;
  11021.      extern unsigned scancode;
  11022.      extern unsigned keymask;
  11023.      
  11024.      static int resoff = 0;
  11025.      static int running = 0;
  11026.      static int popflg = 0;
  11027.      static int diskflag = 0;
  11028.      static int kbval;
  11029.      static int cflag;
  11030.      
  11031.      void dores(void);
  11032.      void pidaddr(void);
  11033.      
  11034.      void resinit()
  11035.      {
  11036.           segread(&seg);
  11037.           myss = seg.ss;
  11038.      
  11039.           rg.h.ah = 0x34;
  11040.           intdos(&rg,&rg);
  11041.           dosseg = _ES;
  11042.           dosbusy = rg.x.bx;
  11043.      
  11044.           mydta = getdta();
  11045.           pidaddr();
  11046.           oldtimer = getvect(0x1c);
  11047.           old28 = getvect(0x28);
  11048.           oldkb = getvect(9);
  11049.           olddisk = getvect(0x13);
  11050.      
  11051.           setvect(0x1c,newtimer);
  11052.           setvect(9,newkb);
  11053.           setvect(0x28,new28);
  11054.           setvect(0x13,newdisk);
  11055.      
  11056.           stack = (sizeprogram - (seg.ds - seg.cs)) * 16 - 300;
  11057.           rg.x.ax = 0x3100;
  11058.           rg.x.dx = sizeprogram;
  11059.           intdos(&rg,&rg);
  11060.      }
  11061.      
  11062.      void interrupt newdisk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)
  11063.      {
  11064.           diskflag++;
  11065.           (*olddisk)();
  11066.           ax = _AX;
  11067.           newcrit();
  11068.           flgs = cflag;
  11069.           --diskflag;
  11070.      }
  11071.      
  11072.      void interrupt newcrit(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)
  11073.      {
  11074.           ax = 0;
  11075.           cflag = flgs;
  11076.      }
  11077.      
  11078.      void interrupt newkb()
  11079.      {
  11080.           if (inportb(0x60) == scancode)
  11081.           {
  11082.                kbval = peekb(0,0x417);
  11083.                if (!resoff && ((kbval & keymask) ^ keymask) == 0)
  11084.                {
  11085.                     kbval = inportb(0x61);
  11086.                     outportb(0x61,kbval | 0x80);
  11087.                     outportb(0x61,kbval);
  11088.                     disable();
  11089.                     outportb(0x20,0x20);
  11090.                     enable();
  11091.                     if (!running)
  11092.                          popflg = 1;
  11093.                     return;
  11094.                }
  11095.           }
  11096.           (*oldkb)();
  11097.      }
  11098.      
  11099.      void interrupt newtimer()
  11100.      {
  11101.           (*oldtimer)();
  11102.           if (popflg && peekb(dosseg,dosbusy) == 0)
  11103.                if(diskflag == 0)
  11104.                {
  11105.                     outportb(0x20,0x20);
  11106.                     popflg = 0;
  11107.                     dores();
  11108.                }
  11109.      }
  11110.      
  11111.      void interrupt new28()
  11112.      {
  11113.           (*old28)();
  11114.           if (popflg && peekb(dosseg,dosbusy) != 0)
  11115.           {
  11116.                popflg = 0;
  11117.                dores();
  11118.           }
  11119.      }
  11120.      
  11121.      resident_psp()
  11122.      {
  11123.           intpsp = peek(dosseg,*pids);
  11124.           for(pp = 0; pp < pidctr; pp++)
  11125.                poke(dosseg,pids[pp],mypsp);
  11126.      }
  11127.      
  11128.      interrupted_psp()
  11129.      {
  11130.           for(pp = 0; pp < pidctr; pp++)
  11131.                poke(dosseg,pids[pp],intpsp);
  11132.      }
  11133.      
  11134.      void dores()
  11135.      {
  11136.           running = 1;
  11137.           disable();
  11138.           intsp = _SP;
  11139.           intss = _SS;
  11140.           _SP = stack;
  11141.           _SS = myss;
  11142.           enable();
  11143.           oldcrit = getvect(0x24);
  11144.           setvect(0x24,newcrit);
  11145.           rg.x.ax = 0x3300;
  11146.           intdos(&rg,&rg);
  11147.           ctrl_break = rg.h.dl;
  11148.           rg.x.ax = 0x3301;
  11149.           rg.h.dl = 0;
  11150.           intdos(&rg,&rg);
  11151.           intdta = getdta();
  11152.           setdta(mydta);
  11153.           resident_psp();
  11154.           popup();
  11155.           interrupted_psp();
  11156.           setdta(intdta);
  11157.           setvect(0x24,oldcrit);
  11158.           rg.x.ax = 0x3301;
  11159.           rg.h.dl = ctrl_break;
  11160.           intdos(&rg,&rg);
  11161.           disable();
  11162.           _SP = intsp;
  11163.           _SS = intss;
  11164.           enable();
  11165.           running = 0;
  11166.      }
  11167.      
  11168.      static int avec = 0;
  11169.      
  11170.      unsigned resident(char *signature,void interrupt(*ifunc)())
  11171.      {
  11172.           char *sg;
  11173.           unsigned df;
  11174.           int vec;
  11175.      
  11176.           segread(&seg);
  11177.           df = seg.ds-seg.cs;
  11178.           for(vec = 0x60; vec < 0x68; vec++)
  11179.           {
  11180.                if (getvect(vec) == NULL)
  11181.                {
  11182.                     if (!avec)
  11183.                          avec = vec;
  11184.                     continue;
  11185.                }
  11186.                for(sg = signature; *sg; sg++)
  11187.                if (*sg != peekb(peek(0,2+vec*4)+df,(unsigned)sg))
  11188.                     break;
  11189.                if (!*sg)
  11190.                     return vec;
  11191.           }
  11192.           if (avec)
  11193.                setvect(avec,ifunc);
  11194.           return 0;
  11195.      }
  11196.      
  11197.      static void pidaddr()
  11198.      {
  11199.           unsigned adr = 0;
  11200.      
  11201.           rg.h.ah = 0x51;
  11202.           intdos(&rg,&rg);
  11203.           mypsp = rg.x.bx;
  11204.           rg.h.ah = 0x52;
  11205.           intdos(&rg,&rg);
  11206.           enddos = _ES;
  11207.           enddos = peek(enddos,rg.x.bx-2);
  11208.           while(pidctr < 2 && (unsigned)((dosseg<<4) + adr) < (enddos
  11209.      <<4))
  11210.           {
  11211.                if (peek(dosseg,adr) == mypsp)
  11212.                {
  11213.                     rg.h.ah = 0x50;
  11214.                     rg.x.bx = mypsp + 1;
  11215.                     intdos(&rg,&rg);
  11216.                     if (peek(dosseg,adr) == mypsp + 1)
  11217.                          pids[pidctr++] = adr;
  11218.                     rg.h.ah = 0x50;
  11219.                     rg.x.bx = mypsp;
  11220.                     intdos(&rg,&rg);
  11221.                }
  11222.                adr++;
  11223.           }
  11224.      }
  11225.      
  11226.      static resterm()
  11227.      {
  11228.           setvect(0x1c,oldtimer);
  11229.           setvect(9,oldkb);
  11230.           setvect(0x28,old28);
  11231.           setvect(0x13,olddisk);
  11232.           setvect(avec,(void interrupt (*)()) 0);
  11233.           rg.h.ah = 0x52;
  11234.           intdos(&rg,&rg);
  11235.           mcbseg = _ES;
  11236.           mcbseg = peek(mcbseg,rg.x.bx-2);
  11237.           segread(&seg);
  11238.           while(peekb(mcbseg,0) == 0x4d)
  11239.           {
  11240.                if(peek(mcbseg,1) == mypsp)
  11241.                {
  11242.                     rg.h.ah = 0x49;
  11243.                     seg.es = mcbseg+1;
  11244.                     intdosx(&rg,&rg,&seg);
  11245.                }
  11246.                mcbseg += peek(mcbseg,3) + 1;
  11247.           }
  11248.      }
  11249.      
  11250.      terminate()
  11251.      {
  11252.           if (getvect(0x13) == (void interrupt (*)()) newdisk)
  11253.                if (getvect(9) == newkb)
  11254.                     if(getvect(0x28) == new28)
  11255.                          if(getvect(0x1c) == newtimer)
  11256.                          {
  11257.                               resterm();
  11258.                               return;
  11259.                          }
  11260.           resoff = 1;
  11261.      }
  11262.      
  11263.      restart()
  11264.      {
  11265.           resoff = 0;
  11266.      }
  11267.      
  11268.      wait()
  11269.      {
  11270.           resoff = 1;
  11271.      }
  11272.      
  11273.      void cursor(int y, int x)
  11274.      {
  11275.           rg.x.ax = 0x0200;
  11276.           rg.x.bx = 0;
  11277.           rg.x.dx = ((y << 8) & 0xff00) + x;
  11278.           int86(16,&rg,&rg);
  11279.      }
  11280.      
  11281.      void curr_cursor(int *y, int *x)
  11282.      {
  11283.           rg.x.ax = 0x0300;
  11284.           rg.x.bx = 0;
  11285.           int86(16,&rg,&rg);
  11286.           *x = rg.h.dl;
  11287.           *y = rg.h.dh;
  11288.      }
  11289.      
  11290.      /*
  11291.         Third module, the simple pop-up address book
  11292.         with mouse support
  11293.      */
  11294.      
  11295.      #include <stdio.h>
  11296.      #include <stdlib.h>
  11297.      #include <io.h>
  11298.      #include <string.h>
  11299.      #include <fcntl.h>
  11300.      #include <sys\stat.h>
  11301.      #include <dos.h>
  11302.      #include <conio.h>
  11303.      #include <graphics.h>
  11304.      #include <bios.h>
  11305.      
  11306.      /* left cannot be less than 3 */
  11307.      #define left   4
  11308.      
  11309.      /* Data structure for records */
  11310.      typedef struct
  11311.      {
  11312.           char name[31];
  11313.           char company[31];
  11314.           char address[31];
  11315.           char area[31];
  11316.           char town[31];
  11317.           char county[31];
  11318.           char post[13];
  11319.           char telephone[16];
  11320.           char fax[16];
  11321.      }
  11322.      data;
  11323.      
  11324.      extern char fpath[];
  11325.      
  11326.      static char scr[4000];
  11327.      
  11328.      static char sbuff[2000];
  11329.      char stext[30];
  11330.      data rec;
  11331.      int handle;
  11332.      int recsize;
  11333.      union REGS inreg,outreg;
  11334.      
  11335.      /*
  11336.           Function prototypes
  11337.      */
  11338.      void FATAL(char *);
  11339.      void OPENDATA(void);
  11340.      void CONTINUE(void);
  11341.      void EXPORT_MULTI(void);
  11342.      void GETDATA(int);
  11343.      int GETOPT(void);
  11344.      void DISPDATA(void);
  11345.      void ADD_REC(void);
  11346.      void PRINT_MULTI(void);
  11347.      void SEARCH(void);
  11348.      void MENU(void);
  11349.      
  11350.      int GET_MOUSE(int *buttons)
  11351.      {
  11352.           inreg.x.ax = 0;
  11353.           int86(0x33,&inreg,&outreg);
  11354.           *buttons = outreg.x.bx;
  11355.           return outreg.x.ax;
  11356.      }
  11357.      
  11358.      void MOUSE_CURSOR(int status)
  11359.      {
  11360.           /* Status = 0 cursor off */
  11361.           /*          1 cursor on */
  11362.      
  11363.           inreg.x.ax = 2 - status;
  11364.           int86(0x33,&inreg,&outreg);
  11365.      }
  11366.      
  11367.      int MOUSE_LOCATION(int *x, int *y)
  11368.      {
  11369.           inreg.x.ax = 3;
  11370.           int86(0x33,&inreg,&outreg);
  11371.      
  11372.           *x = outreg.x.cx / 8;
  11373.           *y = outreg.x.dx / 8;
  11374.      
  11375.           return outreg.x.bx;
  11376.      }
  11377.      
  11378.      int GETOPT()
  11379.      {
  11380.           int result;
  11381.           int x;
  11382.           int y;
  11383.      
  11384.           do
  11385.           {
  11386.                do
  11387.                {
  11388.                     result = MOUSE_LOCATION(&x,&y);
  11389.                     if (result & 1)
  11390.                     {
  11391.                          if (x >= 52 && x <= 53 && y >= 7 && y <= 15)
  11392.                               return y - 7;
  11393.                          if (x >= 4 && x <= 40 && y >= 7 && y <= 14)
  11394.                               return y + 10;
  11395.      
  11396.                          if (x >= 4 && x <= 40 && y == 15)
  11397.                               return y + 10;
  11398.                     }
  11399.                }
  11400.                while(!bioskey(1));
  11401.      
  11402.                result = bioskey(0);
  11403.                x = result & 0xff;
  11404.                if (x == 0)
  11405.                {
  11406.                     result = result >> 8;
  11407.                     result -= 60;
  11408.                }
  11409.           }
  11410.           while(result < 0 || result > 8);
  11411.           return result;
  11412.      }
  11413.      
  11414.      void setvideo(unsigned char mode)
  11415.      {
  11416.           /* Sets the video display mode     and clears the screen */
  11417.      
  11418.           inreg.h.al = mode;
  11419.           inreg.h.ah = 0x00;
  11420.           int86(0x10, &inreg, &outreg);
  11421.      }
  11422.      
  11423.      
  11424.      int activepage(void)
  11425.      {
  11426.           /* Returns the currently selected video display page */
  11427.      
  11428.           union REGS inreg,outreg;
  11429.      
  11430.           inreg.h.ah = 0x0F;
  11431.           int86(0x10, &inreg, &outreg);
  11432.           return(outreg.h.bh);
  11433.      }
  11434.      
  11435.      void print(char *str)
  11436.      {
  11437.           /*
  11438.              Prints characters only directly to the current display page
  11439.              starting at the current cursor position. The cursor is not
  11440.              advanced.
  11441.              This function assumes a colour display card. For use with a
  11442.              monochrome display card change 0xB800 to read 0xB000
  11443.           */
  11444.      
  11445.           int page;
  11446.           int offset;
  11447.           unsigned row;
  11448.           unsigned col;
  11449.           char far *ptr;
  11450.      
  11451.           page = activepage();
  11452.           curr_cursor(&row,&col);
  11453.      
  11454.           offset = page * 4000 + row * 160 + col * 2;
  11455.      
  11456.           ptr = MK_FP(0xB800,offset);
  11457.      
  11458.           while(*str)
  11459.           {
  11460.                *ptr++= *str++;
  11461.                ptr++;
  11462.           }
  11463.      }
  11464.      
  11465.      
  11466.      void TRUESHADE(int lef, int top, int right, int bottom)
  11467.      {
  11468.           int n;
  11469.      
  11470.           /* True Shading of a screen block */
  11471.      
  11472.           gettext(lef,top,right,bottom,sbuff);
  11473.           for(n = 1; n < 2000; n+= 2)
  11474.                sbuff[n] = 7;
  11475.           puttext(lef,top,right,bottom,sbuff);
  11476.      }
  11477.      
  11478.      void DBOX(int l, int t, int r, int b)
  11479.      {
  11480.           /* Draws a double line box around the described area */
  11481.      
  11482.           int n;
  11483.      
  11484.           cursor(t,l);
  11485.           print("É");
  11486.           for(n = 1; n < r - l; n++)
  11487.           {
  11488.                cursor(t,l + n);
  11489.                print("I");
  11490.           }
  11491.           cursor(t,r);
  11492.           print("»");
  11493.      
  11494.           for (n = t + 1; n < b; n++)
  11495.           {
  11496.                cursor(n,l);
  11497.                print("º");
  11498.                cursor(n,r);
  11499.                print("º");
  11500.           }
  11501.           cursor(b,l);
  11502.           print("E");
  11503.           for(n = 1; n < r - l; n++)
  11504.           {
  11505.                cursor(b,l+n);
  11506.                print("I");
  11507.           }
  11508.           cursor(b,r);
  11509.           print("¼");
  11510.      }
  11511.      
  11512.      int INPUT(char *text,unsigned length)
  11513.      {
  11514.           /* Receive a string from the operator */
  11515.      
  11516.           unsigned key_pos;
  11517.           int key;
  11518.           unsigned start_row;
  11519.           unsigned start_col;
  11520.           unsigned end;
  11521.           char temp[80];
  11522.           char *p;
  11523.      
  11524.           curr_cursor(&start_row,&start_col);
  11525.      
  11526.           key_pos = 0;
  11527.           end = strlen(text);
  11528.           for(;;)
  11529.           {
  11530.                key = bioskey(0);
  11531.                if ((key & 0xFF) == 0)
  11532.                {
  11533.                     key = key >> 8;
  11534.                     if (key == 79)
  11535.                     {
  11536.                          while(key_pos < end)
  11537.                               key_pos++;
  11538.                          cursor(start_row,start_col + key_pos);
  11539.                     }
  11540.                     else
  11541.                     if (key == 71)
  11542.                     {
  11543.                          key_pos = 0;
  11544.                          cursor(start_row,start_col);
  11545.                     }
  11546.                     else
  11547.                     if ((key == 75) && (key_pos > 0))
  11548.                     {
  11549.                          key_pos--;
  11550.                          cursor(start_row,start_col + key_pos);
  11551.                     }
  11552.                     else
  11553.                     if ((key == 77) && (key_pos < end))
  11554.                     {
  11555.                          key_pos++;
  11556.                          cursor(start_row,start_col + key_pos);
  11557.                     }
  11558.                     else
  11559.                     if (key == 83)
  11560.                     {
  11561.                          p = text + key_pos;
  11562.                          while(*(p+1))
  11563.                          {
  11564.                               *p = *(p+1);
  11565.                               p++;
  11566.                          }
  11567.                          *p = 32;
  11568.                          if (end > 0)
  11569.                               end--;
  11570.                          cursor(start_row,start_col);
  11571.                          cprintf(text);
  11572.                          cprintf(" ");
  11573.                          if ((key_pos > 0) && (key_pos == end))
  11574.                               key_pos--;
  11575.                          cursor(start_row,start_col + key_pos);
  11576.                     }
  11577.                }
  11578.                else
  11579.                {
  11580.                     key = key & 0xFF;
  11581.                     if (key == 13 || key == 27)
  11582.                          break;
  11583.                     else
  11584.                     if ((key == 8) && (key_pos > 0))
  11585.                     {
  11586.                          end--;
  11587.                          key_pos--;
  11588.                          text[key_pos--] = '\0';
  11589.                          strcpy(temp,text);
  11590.                          p = text + key_pos + 2;
  11591.                          strcat(temp,p);
  11592.                          strcpy(text,temp);
  11593.                          cursor(start_row,start_col);
  11594.                          cprintf("%-*.*s",length,length,text);
  11595.                          key_pos++;
  11596.                          cursor(start_row,start_col + key_pos);
  11597.                     }
  11598.                     else
  11599.                     if ((key > 31) && (key_pos < length)  &&
  11600.                        (start_col + key_pos < 80))
  11601.                     {
  11602.                          if (key_pos <= end)
  11603.                          {
  11604.                               p = text + key_pos;
  11605.                               memmove(p+1,p,end - key_pos);
  11606.                               if (end < length)
  11607.                                    end++;
  11608.                               text[end] = '\0';
  11609.                          }
  11610.                          text[key_pos++] = (char)key;
  11611.                          if (key_pos > end)
  11612.                          {
  11613.                               end++;
  11614.                               text[end] = '\0';
  11615.                          }
  11616.                          cursor(start_row,start_col);
  11617.                          cprintf("%-*.*s",length,length,text);
  11618.                          cursor(start_row,start_col + key_pos);
  11619.                     }
  11620.                }
  11621.           }
  11622.           text[end] = '\0';
  11623.           return key;
  11624.      }
  11625.      
  11626.      void FATAL(char *error)
  11627.      {
  11628.           /* A fatal error has occured */
  11629.      
  11630.           printf("\nFATAL ERROR: %s",error);
  11631.           exit(0);
  11632.      }
  11633.      
  11634.      void OPENDATA()
  11635.      {
  11636.           /* Check for existence of data file and if not create it */
  11637.           /* otherwise open it for reading/writing at end of file */
  11638.      
  11639.           handle = open(fpath,O_RDWR,S_IWRITE);
  11640.      
  11641.           if (handle == -1)
  11642.           {
  11643.                handle = open(fpath,O_RDWR|O_CREAT,S_IWRITE);
  11644.                if (handle == -1)
  11645.                     FATAL("Unable to create data file");
  11646.           }
  11647.           /* Read in first rec */
  11648.           read(handle,&rec,recsize);
  11649.      }
  11650.      
  11651.      void CLOSEDATA()
  11652.      {
  11653.           close(handle);
  11654.      }
  11655.      
  11656.      void GETDATA(int start)
  11657.      {
  11658.           /* Get address data from operator */
  11659.      
  11660.           textcolor(BLACK);
  11661.           textbackground(GREEN);
  11662.           gotoxy(left,8);
  11663.           print("Name ");
  11664.           gotoxy(left,9);
  11665.           print("Company ");
  11666.           gotoxy(left,10);
  11667.           print("Address ");
  11668.           gotoxy(left,11);
  11669.           print("Area ");
  11670.           gotoxy(left,12);
  11671.           print("Town ");
  11672.           gotoxy(left,13);
  11673.           print("County ");
  11674.           gotoxy(left,14);
  11675.           print("Post Code ");
  11676.           gotoxy(left,15);
  11677.           print("Telephone ");
  11678.           gotoxy(left,16);
  11679.           print("Fax ");
  11680.      
  11681.           switch(start)
  11682.           {
  11683.                case 0: gotoxy(left + 10,8);
  11684.                     if(INPUT(rec.name,30) == 27)
  11685.                          break;
  11686.                case 1: gotoxy(left + 10,9);
  11687.                     if(INPUT(rec.company,30) == 27)
  11688.                          break;
  11689.                case 2: gotoxy(left + 10,10);
  11690.                     if(INPUT(rec.address,30) == 27)
  11691.                          break;
  11692.                case 3: gotoxy(left + 10,11);
  11693.                     if(INPUT(rec.area,30) == 27)
  11694.                          break;
  11695.                case 4: gotoxy(left + 10,12);
  11696.                     if(INPUT(rec.town,30) == 27)
  11697.                          break;
  11698.                case 5: gotoxy(left + 10,13);
  11699.                     if(INPUT(rec.county,30) == 27)
  11700.                          break;
  11701.                case 6: gotoxy(left + 10,14);
  11702.                     if(INPUT(rec.post,12) == 27)
  11703.                          break;
  11704.                case 7: gotoxy(left + 10,15);
  11705.                     if(INPUT(rec.telephone,15) == 27)
  11706.                          break;
  11707.                case 8: gotoxy(left + 10,16);
  11708.                     INPUT(rec.fax,15);
  11709.                     break;
  11710.           }
  11711.           textcolor(WHITE);
  11712.           textbackground(RED);
  11713.           gotoxy(left + 23,21);
  11714.           print("                                                 ");
  11715.      }
  11716.      
  11717.      void DISPDATA()
  11718.      {
  11719.           /* Display address data */
  11720.           textcolor(BLACK);
  11721.           textbackground(GREEN);
  11722.           cursor(7,3);
  11723.           cprintf("Name    %-30.30s",rec.name);
  11724.           cursor(8,3);
  11725.           cprintf("Company   %-30.30s",rec.company);
  11726.           cursor(9,3);
  11727.           cprintf("Address   %-30.30s",rec.address);
  11728.           cursor(10,3);
  11729.           cprintf("Area    %-30.30s",rec.area);
  11730.           cursor(11,3);
  11731.           cprintf("Town    %-30.30s",rec.town);
  11732.           cursor(12,3);
  11733.           cprintf("County     %-30.30s",rec.county);
  11734.           cursor(13,3);
  11735.           cprintf("Post Code %-30.30s",rec.post);
  11736.           cursor(14,3);
  11737.           cprintf("Telephone %-30.30s",rec.telephone);
  11738.           cursor(15,3);
  11739.           cprintf("Fax      %-30.30s",rec.fax);
  11740.      }
  11741.      
  11742.      int LOCATE(char *text)
  11743.      {
  11744.           int result;
  11745.      
  11746.           do
  11747.           {
  11748.                /* Read rec into memory */
  11749.                result = read(handle,&rec,recsize);
  11750.                if (result > 0)
  11751.                {
  11752.                     /* Scan rec for matching data */
  11753.                     if (strstr(strupr(rec.name),text) != NULL)
  11754.                          return(1);
  11755.                     if (strstr(strupr(rec.company),text) != NULL)
  11756.                          return(1);
  11757.                     if (strstr(strupr(rec.address),text) != NULL)
  11758.                          return(1);
  11759.                     if (strstr(strupr(rec.area),text) != NULL)
  11760.                          return(1);
  11761.                     if (strstr(strupr(rec.town),text) != NULL)
  11762.                          return(1);
  11763.                     if (strstr(strupr(rec.county),text) != NULL)
  11764.                          return(1);
  11765.                     if (strstr(strupr(rec.post),text) != NULL)
  11766.                          return(1);
  11767.                     if (strstr(strupr(rec.telephone),text) != NULL)
  11768.                          return(1);
  11769.                     if (strstr(strupr(rec.fax),text) != NULL)
  11770.                          return(1);
  11771.                }
  11772.           }
  11773.           while(result > 0);
  11774.           return(0);
  11775.      }
  11776.      
  11777.      void SEARCH()
  11778.      {
  11779.           int result;
  11780.      
  11781.           gotoxy(left,21);
  11782.           textcolor(WHITE);
  11783.           textbackground(RED);
  11784.           cprintf("Enter data to search for ");
  11785.           strcpy(stext,"");
  11786.           INPUT(stext,30);
  11787.           if (*stext == 0)
  11788.           {
  11789.                gotoxy(left,21);
  11790.                cprintf("%70c",32);
  11791.                return;
  11792.           }
  11793.           gotoxy(left,21);
  11794.           textcolor(WHITE);
  11795.           textbackground(RED);
  11796.           cprintf("Searching for %s Please Wait....",stext);
  11797.           strupr(stext);
  11798.           /* Locate start of file */
  11799.           lseek(handle,0,SEEK_SET);
  11800.           result = LOCATE(stext);
  11801.           if (result == 0)
  11802.           {
  11803.                gotoxy(left,21);
  11804.                cprintf("%70c",32);
  11805.                gotoxy(left + 27,21);
  11806.                cprintf("NO MATCHING RECORDS");
  11807.                gotoxy(left + 24,22);
  11808.                cprintf("Press RETURN to Continue");
  11809.                bioskey(0);
  11810.                gotoxy(left,21);
  11811.                cprintf("%70c",32);
  11812.                gotoxy(left,22);
  11813.                cprintf("%70c",32);
  11814.           }
  11815.           else
  11816.           {
  11817.                lseek(handle,0 - recsize,SEEK_CUR);
  11818.                read(handle,&rec,recsize);
  11819.                DISPDATA();
  11820.           }
  11821.           textcolor(WHITE);
  11822.           textbackground(RED);
  11823.           gotoxy(left,21);
  11824.           cprintf("%70c",32);
  11825.           textcolor(BLACK);
  11826.           textbackground(GREEN);
  11827.      }
  11828.      
  11829.      void CONTINUE()
  11830.      {
  11831.           int result;
  11832.           long curpos;
  11833.      
  11834.           curpos = tell(handle) - recsize;
  11835.      
  11836.           result = LOCATE(stext);
  11837.           textcolor(WHITE);
  11838.           textbackground(RED);
  11839.           if (result == 0)
  11840.           {
  11841.                gotoxy(left + 24,21);
  11842.                cprintf("NO MORE MATCHING RECORDS");
  11843.                gotoxy(left + 24,22);
  11844.                cprintf("Press RETURN to Continue");
  11845.                bioskey(0);
  11846.                gotoxy(left,21);
  11847.                cprintf("%70c",32);
  11848.                gotoxy(left,22);
  11849.                cprintf("%70c",32);
  11850.                lseek(handle,curpos,SEEK_SET);
  11851.                read(handle,&rec,recsize);
  11852.                DISPDATA();
  11853.           }
  11854.           else
  11855.           {
  11856.                lseek(handle,0 - recsize,SEEK_CUR);
  11857.                read(handle,&rec,recsize);
  11858.                DISPDATA();
  11859.           }
  11860.           textcolor(WHITE);
  11861.           textbackground(RED);
  11862.           gotoxy(left,21);
  11863.           cprintf("%70c",32);
  11864.           gotoxy(left,22);
  11865.           cprintf("                                           ");
  11866.           textcolor(BLACK);
  11867.           textbackground(GREEN);
  11868.      }
  11869.      
  11870.      void PRINT_MULTI()
  11871.      {
  11872.           data buffer;
  11873.           char destination[60];
  11874.           char text[5];
  11875.           int result;
  11876.           int ok;
  11877.           int ok2;
  11878.           int blanks;
  11879.           int total_lines;
  11880.           char *p;
  11881.           FILE *fp;
  11882.      
  11883.           textcolor(WHITE);
  11884.           textbackground(RED);
  11885.           gotoxy(left + 23,21);
  11886.           cprintf("Enter selection criteria");
  11887.      
  11888.           /* Clear existing rec details */
  11889.           memset(&rec,0,recsize);
  11890.      
  11891.           DISPDATA();
  11892.           GETDATA(0);
  11893.      
  11894.           textcolor(WHITE);
  11895.           textbackground(RED);
  11896.           gotoxy(left,21);
  11897.           cprintf("Enter report destination PRN");
  11898.           strcpy(destination,"PRN");
  11899.           gotoxy(left,22);
  11900.           cprintf("Enter Address length in lines 18");
  11901.           strcpy(text,"18");
  11902.           gotoxy(left + 25,21);
  11903.           INPUT(destination,40);
  11904.           gotoxy(left +30,22);
  11905.           INPUT(text,2);
  11906.           gotoxy(left,21);
  11907.           cprintf("%72c",32);
  11908.           gotoxy(left,22);
  11909.           cprintf("%72c",32);
  11910.      
  11911.           total_lines = atoi(text) - 6;
  11912.           if (total_lines < 0)
  11913.                total_lines = 0;
  11914.      
  11915.           fp = fopen(destination,"w+");
  11916.           if (fp == NULL)
  11917.           {
  11918.                gotoxy(left,21);
  11919.                cprintf("Unable to print to %s",destination);
  11920.                gotoxy(left,22);
  11921.                cprintf("Press RETURN to Continue");
  11922.                bioskey(0);
  11923.                gotoxy(left,21);
  11924.                cprintf("%78c",32);
  11925.                gotoxy(left,22);
  11926.                cprintf("                          ");
  11927.           }
  11928.      
  11929.           /* Locate start of file */
  11930.           lseek(handle,0,SEEK_SET);
  11931.      
  11932.           do
  11933.           {
  11934.                /* Read rec into memory */
  11935.                result = read(handle,&buffer,recsize);
  11936.                if (result > 0)
  11937.                {
  11938.                     ok = 1;
  11939.                     /* Scan rec for matching data */
  11940.                     if (*rec.name)
  11941.                          if (stricmp(buffer.name,rec.name))
  11942.                               ok = 0;
  11943.                     if (*rec.company)
  11944.                          if (stricmp(buffer.company,rec.company))
  11945.                               ok = 0;
  11946.                     if (*rec.address)
  11947.                          if (stricmp(buffer.address,rec.address))
  11948.                               ok = 0;
  11949.                     if (*rec.area)
  11950.                          if (stricmp(buffer.area,rec.area))
  11951.                               ok = 0;
  11952.                     if (*rec.town)
  11953.                          if (stricmp(buffer.town,rec.town))
  11954.                               ok = 0;
  11955.                     if (*rec.county)
  11956.                          if (stricmp(buffer.county,rec.county))
  11957.                               ok = 0;
  11958.                     if (*rec.post)
  11959.                          if (stricmp(buffer.post,rec.post))
  11960.                          ok = 0;
  11961.                     if (*rec.telephone)
  11962.                          if (stricmp(buffer.telephone,rec.telephone))
  11963.                               ok = 0;
  11964.                     if (*rec.fax)
  11965.                          if (stricmp(buffer.fax,rec.fax))
  11966.                               ok = 0;
  11967.                     if (ok)
  11968.                     {
  11969.                          blanks = total_lines;
  11970.                          p = buffer.name;
  11971.                          ok2 = 0;
  11972.                          while(*p)
  11973.                          {
  11974.                               if (*p != 32)
  11975.                               {
  11976.                                    ok2 = 1;
  11977.                                    break;
  11978.                               }
  11979.                               p++;
  11980.                          }
  11981.                          if (!ok2)
  11982.                               blanks++;
  11983.                          else
  11984.                               fprintf(fp,"%s\n",buffer.name);
  11985.                          p = buffer.company;
  11986.                          ok2 = 0;
  11987.                          while(*p)
  11988.                          {
  11989.                               if (*p != 32)
  11990.                               {
  11991.                                    ok2 = 1;
  11992.                                    break;
  11993.                               }
  11994.                               p++;
  11995.                          }
  11996.                          if (!ok2)
  11997.                               blanks++;
  11998.                          else
  11999.                               fprintf(fp,"%s\n",buffer.company);
  12000.                          p = buffer.address;
  12001.                          ok2 = 0;
  12002.      
  12003.                          while(*p)
  12004.                          {
  12005.                               if (*p != 32)
  12006.                               {
  12007.                                    ok2 = 1;
  12008.                                    break;
  12009.                               }
  12010.                               p++;
  12011.                          }
  12012.                          if (!ok2)
  12013.                               blanks++;
  12014.                          else
  12015.                               fprintf(fp,"%s\n",buffer.address);
  12016.                          p = buffer.area;
  12017.                          ok2 = 0;
  12018.                          while(*p)
  12019.                          {
  12020.                               if (*p != 32)
  12021.                               {
  12022.                                    ok2 = 1;
  12023.                                    break;
  12024.                               }
  12025.                               p++;
  12026.                          }
  12027.                          if (!ok2)
  12028.                               blanks++;
  12029.                          else
  12030.                               fprintf(fp,"%s\n",buffer.area);
  12031.                          p = buffer.town;
  12032.                          ok2 = 0;
  12033.                          while(*p)
  12034.                          {
  12035.                               if (*p != 32)
  12036.                               {
  12037.                                    ok2 = 1;
  12038.                                    break;
  12039.                               }
  12040.                               p++;
  12041.                          }
  12042.                          if (!ok2)
  12043.                               blanks++;
  12044.                          else
  12045.                               fprintf(fp,"%s\n",buffer.town);
  12046.                          p = buffer.county;
  12047.                          ok2 = 0;
  12048.      
  12049.                          while(*p)
  12050.                          {
  12051.                               if (*p != 32)
  12052.                               {
  12053.                                    ok2 = 1;
  12054.                                    break;
  12055.                               }
  12056.                               p++;
  12057.                          }
  12058.                          if (!ok2)
  12059.                               blanks++;
  12060.                          else
  12061.                               fprintf(fp,"%s\n",buffer.county);
  12062.                          p = buffer.post;
  12063.                          ok2 = 0;
  12064.                          while(*p)
  12065.                          {
  12066.                               if (*p != 32)
  12067.                               {
  12068.                                    ok2 = 1;
  12069.                                    break;
  12070.                               }
  12071.                               p++;
  12072.                          }
  12073.                          if (!ok2)
  12074.                               blanks++;
  12075.                          else
  12076.                               fprintf(fp,"%s\n",buffer.post);
  12077.                          while(blanks)
  12078.                          {
  12079.                               fprintf(fp,"\n");
  12080.                               blanks--;
  12081.                          }
  12082.                     }
  12083.                }
  12084.           }
  12085.           while(result > 0);
  12086.           fclose(fp);
  12087.           lseek(handle,0,SEEK_SET);
  12088.           read(handle,&rec,recsize);
  12089.           DISPDATA();
  12090.      }
  12091.      
  12092.      void EXPORT_MULTI()
  12093.      {
  12094.           data buffer;
  12095.           char destination[60];
  12096.           int result;
  12097.           int ok;
  12098.           FILE *fp;
  12099.      
  12100.           textcolor(WHITE);
  12101.           textbackground(RED);
  12102.           gotoxy(left + 23,21);
  12103.           cprintf("Enter selection criteria");
  12104.      
  12105.           /* Clear existing rec details */
  12106.           memset(&rec,0,recsize);
  12107.      
  12108.           DISPDATA();
  12109.           GETDATA(0);
  12110.      
  12111.           textcolor(WHITE);
  12112.           textbackground(RED);
  12113.           gotoxy(left,21);
  12114.           cprintf("Enter export file address.txt");
  12115.           strcpy(destination,"address.txt");
  12116.           gotoxy(left + 18,21);
  12117.           INPUT(destination,59);
  12118.           gotoxy(left,21);
  12119.           cprintf("%70c",32);
  12120.      
  12121.           fp = fopen(destination,"w+");
  12122.           if (fp == NULL)
  12123.           {
  12124.                gotoxy(left,21);
  12125.                cprintf("Unable to print to %s",destination);
  12126.                gotoxy(left,22);
  12127.                cprintf("Press RETURN to Continue");
  12128.                bioskey(0);
  12129.                gotoxy(left,21);
  12130.                cprintf("%78c",32);
  12131.                gotoxy(left,22);
  12132.                cprintf("                          ");
  12133.           }
  12134.           /* Locate start of file */
  12135.           lseek(handle,0,SEEK_SET);
  12136.      
  12137.           do
  12138.           {
  12139.                /* Read rec into memory */
  12140.                result = read(handle,&buffer,recsize);
  12141.                if (result > 0)
  12142.                {
  12143.                     ok = 1;
  12144.                     /* Scan rec for matching data */
  12145.                     if (*rec.name)
  12146.                          if (stricmp(buffer.name,rec.name))
  12147.                               ok = 0;
  12148.                     if (*rec.company)
  12149.                          if (stricmp(buffer.company,rec.company))
  12150.                               ok = 0;
  12151.                     if (*rec.address)
  12152.                          if (stricmp(buffer.address,rec.address))
  12153.                               ok = 0;
  12154.                     if (*rec.area)
  12155.                          if (stricmp(buffer.area,rec.area))
  12156.                               ok = 0;
  12157.                     if (*rec.town)
  12158.                          if (stricmp(buffer.town,rec.town))
  12159.                               ok = 0;
  12160.                     if (*rec.county)
  12161.                          if (stricmp(buffer.county,rec.county))
  12162.                               ok = 0;
  12163.                     if (*rec.post)
  12164.                          if (stricmp(buffer.post,rec.post))
  12165.                          ok = 0;
  12166.                     if (*rec.telephone)
  12167.                          if (stricmp(buffer.telephone,rec.telephone))
  12168.                               ok = 0;
  12169.                     if (*rec.fax)
  12170.                          if (stricmp(buffer.fax,rec.fax))
  12171.                               ok = 0;
  12172.                     if (ok)
  12173.                     {
  12174.                          fprintf(fp,"\"%s\",",buffer.name);
  12175.                          fprintf(fp,"\"%s\",",buffer.company);
  12176.                          fprintf(fp,"\"%s\",",buffer.address);
  12177.                          fprintf(fp,"\"%s\",",buffer.area);
  12178.                          fprintf(fp,"\"%s\",",buffer.town);
  12179.                          fprintf(fp,"\"%s\",",buffer.county);
  12180.                          fprintf(fp,"\"%s\",",buffer.post);
  12181.                          fprintf(fp,"\"%s\",",buffer.telephone);
  12182.                          fprintf(fp,"\"%s\"\n",buffer.fax);
  12183.      
  12184.                     }
  12185.                }
  12186.           }
  12187.      
  12188.           while(result > 0);
  12189.           fclose(fp);
  12190.           lseek(handle,0,SEEK_SET);
  12191.           read(handle,&rec,recsize);
  12192.           DISPDATA();
  12193.      }
  12194.      
  12195.      void MENU()
  12196.      {
  12197.           int option;
  12198.           long result;
  12199.           long end;
  12200.           int new;
  12201.      
  12202.           do
  12203.           {
  12204.                cursor(21,26);
  12205.                print("Select option (F2 - F10)");
  12206.                cursor(7,52);
  12207.                print("F2 Next record");
  12208.                cursor(8,52);
  12209.                print("F3 Previous record");
  12210.                cursor(9,52);
  12211.                print("F4 Amend record");
  12212.                cursor(10,52);
  12213.                print("F5 Add new record");
  12214.                cursor(11,52);
  12215.                print("F6 Search");
  12216.                cursor(12,52);
  12217.                print("F7 Continue search");
  12218.                cursor(13,52);
  12219.                print("F8 Print address labels");
  12220.                cursor(14,52);
  12221.                print("F9 Export records");
  12222.                cursor(15,52);
  12223.                print("F10 Exit");
  12224.                MOUSE_CURSOR(1);
  12225.                option = GETOPT();
  12226.                MOUSE_CURSOR(0);
  12227.      
  12228.                switch(option)
  12229.                {
  12230.                     case 0 : /* Next rec */
  12231.                           result = read(handle,&rec,recsize);
  12232.                           if (!result)
  12233.                           {
  12234.                               lseek(handle,0,SEEK_SET);
  12235.                                result = read(handle,&rec,recsize);
  12236.                           }
  12237.                           DISPDATA();
  12238.                           break;
  12239.      
  12240.                     case 1 : /* Previous rec */
  12241.                          result = lseek(handle,0 - recsize * 2,SEEK_CUR);
  12242.                          if (result <= -1)
  12243.                               lseek(handle,0 - recsize,SEEK_END);
  12244.                          result = read(handle,&rec,recsize);
  12245.                          DISPDATA();
  12246.                          break;
  12247.  
  12248.                     case 3 : /* Add rec */
  12249.                           lseek(handle,0,SEEK_END);
  12250.                           memset(&rec,0,recsize);
  12251.                           DISPDATA();
  12252.      
  12253.                     case 2 : /* Amend current rec */
  12254.                           new = 1;
  12255.                           if (*rec.name)
  12256.                               new = 0;
  12257.                           else
  12258.                           if (*rec.company)
  12259.                               new = 0;
  12260.                           else
  12261.                           if (*rec.address)
  12262.                               new = 0;
  12263.                           else
  12264.                           if (*rec.area)
  12265.                               new = 0;
  12266.                           else
  12267.                           if (*rec.town)
  12268.                               new = 0;
  12269.                           else
  12270.                           if (*rec.county)
  12271.                               new = 0;
  12272.                           else
  12273.                           if (*rec.post)
  12274.                               new = 0;
  12275.                           else
  12276.                           if (*rec.telephone)
  12277.                               new = 0;
  12278.                           else
  12279.                           if (*rec.fax)
  12280.                               new = 0;
  12281.                           result = tell(handle);
  12282.                           lseek(handle,0,SEEK_END);
  12283.                           end = tell(handle);
  12284.      
  12285.                           /* Back to original position */
  12286.                           lseek(handle,result,SEEK_SET);
  12287.      
  12288.                           /* If not at end of file, && !new rewind one
  12289. rec */
  12290.                           if (result != end || ! new)
  12291.                               result = lseek(handle,0 -
  12292. recsize,SEEK_CUR);
  12293.                           result = tell(handle);
  12294.                           gotoxy(left + 22,21);
  12295.                           print(" Enter address details  ");
  12296.                           GETDATA(0);
  12297.                           if (*rec.name || *rec.company)
  12298.                               result =  write(handle,&rec,recsize);
  12299.                           break;
  12300.  
  12301.                     case 4 : /* Search */
  12302.                           gotoxy(left + 22,21);
  12303.                           print("                           ");
  12304.                           SEARCH();
  12305.                           break;
  12306.      
  12307.                     case 5 : /* Continue */
  12308.                           gotoxy(left + 22,21);
  12309.                           print("                           ");
  12310.                           CONTINUE();
  12311.                           break;
  12312.      
  12313.                     case 6 : /* Print */
  12314.                           gotoxy(left + 22,21);
  12315.                           print("                           ");
  12316.                           PRINT_MULTI();
  12317.                           break;
  12318.  
  12319.                     case 7 : /* Export */
  12320.                           gotoxy(left + 22,21);
  12321.                           print("                           ");
  12322.                           EXPORT_MULTI();
  12323.                           break;
  12324.      
  12325.                     case 8 : /* Exit */
  12326.                           break;
  12327.      
  12328.                     default: /* Amend current rec */
  12329.                           new = 1;
  12330.                           if (*rec.name)
  12331.                               new = 0;
  12332.                           else
  12333.                           if (*rec.company)
  12334.                               new = 0;
  12335.                           else
  12336.                           if (*rec.address)
  12337.                               new = 0;
  12338.                           else
  12339.                           if (*rec.area)
  12340.                               new = 0;
  12341.                           else
  12342.                           if (*rec.town)
  12343.                               new = 0;
  12344.                           else
  12345.                           if (*rec.county)
  12346.                               new = 0;
  12347.                           else
  12348.                           if (*rec.post)
  12349.                               new = 0;
  12350.                           else
  12351.                           if (*rec.telephone)
  12352.                               new = 0;
  12353.                           else
  12354.                           if (*rec.fax)
  12355.                               new = 0;
  12356.                           result = tell(handle);
  12357.                           lseek(handle,0,SEEK_END);
  12358.                           end = tell(handle);
  12359.  
  12360.                           /* Back to original position */
  12361.                           lseek(handle,result,SEEK_SET);
  12362.  
  12363.                           /* If not at end of file, && !new rewind one
  12364. rec */
  12365.                           if (result != end || ! new)
  12366.                               result = lseek(handle,0 -
  12367. recsize,SEEK_CUR);
  12368.                           result = tell(handle);
  12369.                           gotoxy(left + 22,21);
  12370.                           print(" Enter address details  ");
  12371.                           GETDATA(option - 17);
  12372.                           if (*rec.name || *rec.company)
  12373.                               result = write(handle,&rec,recsize);
  12374.                           option = -1;
  12375.                           break;
  12376.  
  12377.                }
  12378.           }
  12379.      
  12380.           while(option != 8);
  12381.      }
  12382.      
  12383.      void exec()
  12384.      {
  12385.           gettext(1,1,80,25,scr);
  12386.           setvideo(3);
  12387.           textbackground(WHITE);
  12388.           textcolor(BLACK);
  12389.           clrscr();
  12390.           recsize = sizeof(data);
  12391.      
  12392.           OPENDATA();
  12393.      
  12394.           TRUESHADE(left,3,79,5);
  12395.           window(left - 2,2 ,78, 4);
  12396.           textcolor(YELLOW);
  12397.           textbackground(MAGENTA);
  12398.           clrscr();
  12399.           DBOX(left - 3, 1, 77, 3);
  12400.           gotoxy(3,2);
  12401.           print("Servile Software             PC ADDRESS BOOK 5.2
  12402.      (c) 1994");
  12403.      
  12404.           TRUESHADE(left,8,left + 43,18);
  12405.           window(left - 2,7 , left + 42, 17);
  12406.           textcolor(BLACK);
  12407.           textbackground(GREEN);
  12408.           clrscr();
  12409.           DBOX(left - 3, 6, left + 41, 16);
  12410.      
  12411.           TRUESHADE(left + 48,8,79,18);
  12412.           window(left + 46, 7 , 78, 17);
  12413.           textbackground(BLUE);
  12414.           textcolor(YELLOW);
  12415.           clrscr();
  12416.           DBOX(left + 45,6,77,16);
  12417.      
  12418.           TRUESHADE(left ,21,79,24);
  12419.           window(left - 2, 20 , 78, 23);
  12420.           textbackground(RED);
  12421.           textcolor(WHITE);
  12422.           clrscr();
  12423.           DBOX(left - 3,19,77,22);
  12424.      
  12425.           window(1,1,80,25);
  12426.           textcolor(BLACK);
  12427.           textbackground(GREEN);
  12428.           DISPDATA();
  12429.      
  12430.           MENU();
  12431.      
  12432.           CLOSEDATA();
  12433.           puttext(1,1,80,25,scr);
  12434.           return;
  12435.      }
  12436.      
  12437.                                     
  12438.                        INTERFACING C WITH CLIPPER
  12439.  
  12440. The Clipper programming language is a popular xBase environment for the
  12441. PC.  However, it lacks many of the facilities available to programmers of
  12442. other languages, and it is quite slow compared to C. Because of this
  12443. there are a large number of third party add-on libraries available for
  12444. Clipper which provide the facilities lacked.
  12445.  
  12446. As a programmer you probably want to write your own library for Clipper,
  12447. or perhaps individual functions to cater for circumstances which Clipper
  12448. cannot handle, such as high resolution graphics.
  12449.  
  12450. Throughout this section, Clipper refers to the Summer `87 Clipper
  12451. compiler, although initial tests show that the functions described here
  12452. work perfectly well with the new Clipper 5 compiler also, we are not in a
  12453. position to guarrantee success!
  12454.  
  12455.  
  12456. COMPILING AND LINKING
  12457. The  Clipper extend functions allow user defined functions to be written
  12458. in C, linked with and used by the Clipper application. The  first
  12459. problem a programmer must address when writing functions in C  to link
  12460. with  a  Clipper  application is that of the  C  compiler's  run  time
  12461. libraries.
  12462.  
  12463. If one is writing functions with Microsoft C,  then most of the required
  12464. run time  library  functions  will be found in the  Clipper.lib  and
  12465. Extend.lib libraries which are part of Clipper.
  12466.  
  12467. If,  however, one is using a different C compiler, such as Borland's
  12468. Turbo C then the run time library routines must be supplied on the link
  12469. line.
  12470.  
  12471. All C functions must be compiled using the large memory model the
  12472. following line is used with Microsoft C
  12473.  
  12474.  
  12475.      cl /c /AL /Zl /Oalt /FPa /Gs <program.c>
  12476.  
  12477. and this compile line may be used with Turbo C
  12478.  
  12479.      tcc -c -ml <program>
  12480.  
  12481. simply substitute <program> for the program name to be compiled.
  12482.  
  12483. Having compiled a C function it must be linked in with the application.
  12484. If the C function was compiled with Microsoft C then the link line will
  12485. look a little like this;
  12486.  
  12487.  
  12488.      LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend
  12489.  
  12490. If  the C function was linked with another C compiler you will also need
  12491. to link in the C run time libraries,  for example to link in the Turbo C
  12492. large memory mode library use the following link line;
  12493.  
  12494.  
  12495.      LINK /SE:500 /NOE program.obj cfunc.obj,,,Clipper Extend cl
  12496.  
  12497. If one is using a number of separately compiled C functions it is a good
  12498. idea to  collect  them in a library.  If you are using Microsoft C then
  12499. you  can simply  create  the  library by using Microsoft Lib.exe with
  12500. the  following command line;
  12501.  
  12502.  
  12503.       LIB mylib +prog1 +prog2, NUL, NUL
  12504.  
  12505. This tells the librarian to add prog1.obj and prog2.obj to a library
  12506. called mylib.lib,   creating  it  if it does not exist.  The NUL
  12507. parameter  is  for supressing the listing file.
  12508.  
  12509.  
  12510. If you have been using another C compiler you should copy the C large
  12511. memory model run time library before adding your functions to it for
  12512. example;
  12513.  
  12514.  
  12515.       COPY C:\TURBOC\LIB\cl.lib mylib.lib
  12516.       LIB mylib +prog1 +prog2, NUL, NUL
  12517.  
  12518. Then when you link your Clipper application you will use a link line
  12519. similar to;
  12520.  
  12521.      LINK /SE:500 /NOE myprog,,,Clipper Extend Mylib
  12522.  
  12523. Often  when  linking C functions with  Clipper applications link errors
  12524. will occur such as those shown below;
  12525.  
  12526.      
  12527.      Microsoft (R) Overlay Linker  Version 3.65
  12528.      Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.
  12529.      
  12530.      
  12531.      LINK : error L2029: Unresolved externals:
  12532.      
  12533.      
  12534.      FIWRQQ in file(s):
  12535.       M:SLIB.LIB(TEST)
  12536.      FIDRQQ in file(s):
  12537.       M:SLIB.LIB(TEST)
  12538.      
  12539.      There were 2 errors detected
  12540.      
  12541.  
  12542. Example Link Errors
  12543.  
  12544. The  errors  shown  here  are  `Unresolved externals',   that  is  they
  12545. are references to functions which are not found in any of the object
  12546. modules  or libraries  specified on the link line.  These occur because
  12547. the C  compilers often  scatter  functions and variables through a number
  12548. of  libraries.   In tracking  these  functions down  use may be made of
  12549. the Microsoft  librarian list file option.  If you run Lib.Exe on the
  12550. Turbo C `emu.lib'  library file and specify a listing file as follows;
  12551.  
  12552.  
  12553.      LIB emu,emu.lst
  12554.  
  12555. The  librarian  will create an ascii file which contains the names  of
  12556. each object module contained in the specified library file, and the names
  12557. of each function and public variable declared in each object module, as
  12558. shown in this listing of Borland's EMU.LIB library.
  12559.  
  12560.  
  12561.      e086_Entry........EMU086      e086_Shortcut.....EMU086
  12562.      e087_Entry........EMU087      e087_Shortcut.....EMU087
  12563.      FIARQQ............EMUINIT          FICRQQ............EMUINIT
  12564.      FIDRQQ............EMUINIT          FIERQQ............EMUINIT
  12565.      FISRQQ............EMUINIT          FIWRQQ............EMUINIT
  12566.      FJARQQ............EMUINIT          FJCRQQ............EMUINIT
  12567.      FJSRQQ............EMUINIT          __EMURESET........EMUINIT
  12568.      
  12569.      
  12570.      EMUINIT        Offset: 00000010H  Code and data size: 1a2H
  12571.      FIARQQ         FICRQQ              FIDRQQ              FIERQQ
  12572.      FISRQQ         FIWRQQ              FJARQQ              FJCRQQ
  12573.      FJSRQQ         __EMURESET
  12574.      
  12575.      EMU086         Offset: 00000470H  Code and data size: 2630H
  12576.        e086_Entry        e086_Shortcut
  12577.      
  12578.      EMU087         Offset: 00003200H  Code and data size: 417H
  12579.        e087_Entry        e087_Shortcut
  12580.      
  12581.  
  12582.  
  12583. Receiving Parameters
  12584.  
  12585. Clipper provides six different functions for receiving parameters in a C
  12586. function. These functions are;
  12587.  
  12588.  
  12589.   Receive a string      char * _parc(int,[int])
  12590.   Receive a Date string char * _pards(int,[int])
  12591.   Receive a logical     int _parl(int,[int])
  12592.   Receive an integer         int _parni(int,[int])
  12593.   Receive a long        long _parnl(int,[int])
  12594.   Receive a double      double _parnd(int,[int])
  12595.  
  12596.  
  12597.  
  12598. To  illustrate  simple  parameter  receiving in a C  function  I  offer
  12599. the following  simple C function which receives two numeric parameters
  12600. from  the calling  Clipper program,  and uses these two numeric
  12601. parameters to set  the size of the cursor.
  12602.  
  12603.  
  12604.      #include <nandef.h>            /* Clipper header files */
  12605.      #include <extend.h>
  12606.      #include <dos.h>                   /* Header file to define REGS */
  12607.      
  12608.      CLIPPER s_curset()
  12609.      {
  12610.           /* Demonstration function to set cursor shape */
  12611.      
  12612.           union REGS inreg,outreg;
  12613.      
  12614.           inreg.h.ah = 0x01;
  12615.           inreg.h.ch = _parni(1);   /* Get integer parameter 1 */
  12616.           inreg.h.cl = _parni(2);   /* Get integer parameter 2 */
  12617.           int86(0x10,&inreg,&outreg);
  12618.           _ret();                        /* Return to Clipper */
  12619.      }
  12620.  
  12621. Clipper  provides four more functions for dealing with received
  12622. parameters;
  12623.  
  12624. _parclen(int,[int])  which returns the length of a string including
  12625. imbedded `\0's,   _parcsiz(int[int])  which returns the length of a
  12626. character  string passed by reference from Clipper, _parinfa(int,[int])
  12627. which returns the type of  a  specified  array  element  or the length of
  12628. an  array,   and  finally _parinfo(int) whic returns the type of a
  12629. parameter.
  12630.  
  12631. The following example function uses _parinfa()  to determine both the
  12632. length of an array passed from a Clipper program,  and the type of each
  12633. element  in the array.  The function then returns to Clipper an integer
  12634. representing the number of defined elements in the array.
  12635.  
  12636.  
  12637.  
  12638.      #include <nandef.h>
  12639.      #include <extend.h>
  12640.      
  12641.      CLIPPER s_alen()
  12642.      {
  12643.           int total;
  12644.           int n;
  12645.           int defined;
  12646.           int type;
  12647.      
  12648.           /* Return the number of defined elements in an array */
  12649.           /* From Clipper use defined = s_alen(arr) */
  12650.      
  12651.           total = _parinfa(1,0); /* Get declared number of elements in
  12652.      array */
  12653.      
  12654.           defined = 0;
  12655.      
  12656.           for (n = 1; n <= total; n++){
  12657.                type = _parinfa(1,n);   /* Get array parameter type */
  12658.                if (type)
  12659.                     defined++;
  12660.           }
  12661.           _retni(defined);              /* Return an integer to Clipper
  12662.      */
  12663.      }
  12664.      
  12665.  
  12666. This  function  goes  one step further to return the  mean  average  of
  12667. all numeric  values  in an array.  Notice the use of _parnd()  to
  12668. retrieve  the numeric  values as doubles.  You may find that because of
  12669. the floating point arithmetic  in  this  function  that it will  only
  12670. work  if  compiled  with Microsoft C.
  12671.  
  12672.  
  12673.      #include <nandef.h>
  12674.      #include <extend.h>
  12675.      
  12676.      CLIPPER s_aave()
  12677.      {
  12678.           int total;
  12679.           int defined;
  12680.           int n;
  12681.           int type;
  12682.           double sum;
  12683.      
  12684.           /* Return the mean average value of numbers in array */
  12685.           /* From Clipper use mean = s_aave(arr)
  12686.      
  12687.      
  12688.           total = _parinfa(1,0);               /* Get declared number of
  12689.      elements */
  12690.      
  12691.           defined = 0;
  12692.      
  12693.           for (n = 1; n <= total; n++){    /* Determine number of defined
  12694.      */
  12695.                type = _parinfa(1,n);            /* elements */
  12696.                if (type == 2)
  12697.                     defined++;
  12698.           }
  12699.      
  12700.           sum = 0;
  12701.      
  12702.           for (n = 1; n <= total; n++){
  12703.                type = _parinfa(1,n);
  12704.                if (type == 2)                  /* Only sum numeric values
  12705.      */
  12706.                     sum += _parnd(1,n);
  12707.           }
  12708.           _retnd(sum / defined);               /* Return a double to
  12709.      Clipper */
  12710.      }
  12711.      
  12712.      
  12713.  
  12714. Returning Values
  12715.  
  12716. The  Clipper  manual  lists seven functions for returning  from  a
  12717. function written in another language. These return functions for C are as
  12718. follows;
  12719.  
  12720.   character        _retc(char *)
  12721.   date        _retds(char *)
  12722.   logical     _retl(int)
  12723.   numeric (int)    _retni(int)
  12724.   numeric (long)   _retnl(long)
  12725.   numeric  (double)     _retnd(double)
  12726.   nothing     _ret(void)
  12727.  
  12728. Omitted  from  the  Clipper manual is the information that  you  may
  12729. return different types of value back from a function! For example,  you
  12730. may wish to return a character string under normal circumstances,  fine
  12731. use _retc().  On error occurences however you can return a logical using
  12732. _retl(). The Clipper program  will  assign the received value to the
  12733. receiving  variable  in  the correct manner.
  12734.  
  12735. The following simple C function returns a random number.  Notice the use
  12736. of integers  which  limits  the range of the function to +-32767.   For
  12737. larger values you should use longs instead of integers.
  12738.  
  12739.  
  12740.      #include <nandef.h>
  12741.      #include <extend.h>
  12742.      #include <dos.h>
  12743.      
  12744.      CLIPPER s_random()
  12745.      {
  12746.           /* Returns a random number between 0 and param1 - 1 */
  12747.           /* From Clipper use x = s_random(param1) */
  12748.      
  12749.           int param1;
  12750.           int x;
  12751.      
  12752.           param1 = _parni(1);
  12753.      
  12754.           x = rand() % param1;
  12755.           _retni(x);
  12756.      }
  12757.      
  12758.  
  12759. This function receives a string from Clipper,  and passes back an upper
  12760. case copy of the string,  leaving the original unchanged.  The maximum
  12761. length  of the  string  which can be processed is determined by the size
  12762. of  target[], here set to 5000 characters.
  12763.  
  12764.  
  12765.      #include <nandef.h>
  12766.      #include <extend.h>
  12767.      
  12768.      CLIPPER s_upper()
  12769.      {
  12770.           /* Returns an upper case copy of string */
  12771.           /* From Clipper use ? s_upper("this is a string")
  12772.      
  12773.           char *p;
  12774.           char *q;
  12775.           char *string;
  12776.           char target[5000];
  12777.           int n;
  12778.      
  12779.           string = _parc(1);
  12780.      
  12781.           p = string;
  12782.           q = target;
  12783.      
  12784.           while(*string){
  12785.                *q++ = toupper(*string);
  12786.                string++;
  12787.           }
  12788.           *q = '\0';
  12789.           string = p;
  12790.           _retc(target);
  12791.      }
  12792.      
  12793.      
  12794. This  function  may be used to change the current DOS directory.  If  it
  12795. is successful  it  returns .T.  to the calling Clipper program,
  12796. otherwise  it returns .F.
  12797.  
  12798.      #include <nandef.h>
  12799.      #include <extend.h>
  12800.      #include <dos.h>
  12801.      
  12802.      CLIPPER s_chdir()
  12803.      {
  12804.           /* Attempts to change the current DOS directory */
  12805.           /* From Clipper use result = s_chdir(path) */
  12806.      
  12807.           union REGS inreg,outreg;
  12808.           struct SREGS segreg;
  12809.      
  12810.           char *path;
  12811.           int x;
  12812.      
  12813.           path = _parc(1);              /* Retrieve string from Clipper
  12814.      */
  12815.      
  12816.           inreg.h.ah = 0x3b;
  12817.           segreg.ds = FP_SEG(path);
  12818.           inreg.x.dx = FP_OFF(path);
  12819.           intdosx(&inreg,&outreg,&segreg);
  12820.      
  12821.           x = outreg.x.ax;
  12822.      
  12823.           if (x == 3)
  12824.                _retl(0);    /* Return logical .F. back to Clipper */
  12825.           else
  12826.                _retl(1);    /* Return logical .T. back to Clipper */
  12827.      }
  12828.      
  12829.  
  12830.  
  12831. Avoiding Unresolved Externals
  12832.  
  12833. As we have already seen, a common problem plaguing the programmer
  12834. interfacing C functions with Clipper programs is Unresolved Externals.
  12835.  
  12836. The  following  example  C function called s_print()   will  not  link
  12837. with Clipper.
  12838.  
  12839.      #include <nandef.h>
  12840.      #include <extend.h>
  12841.      #include <stdio.h>
  12842.      
  12843.      CLIPPER s_print()
  12844.      {
  12845.           char *x;
  12846.      
  12847.           x = _parc(1);
  12848.      
  12849.           printf("\nI received %s from Clipper.\n",x);
  12850.      
  12851.           _ret();
  12852.      }
  12853.      
  12854.  
  12855. The linker gives you the following reply;
  12856.  
  12857.      Microsoft (R) Overlay Linker  Version 3.65
  12858.      Copyright (C) Microsoft Corp 1983-1988.  All rights reserved.
  12859.      
  12860.      M:SLIB.LIB(IOERROR) : error L2025: __doserrno : symbol defined more
  12861.      than once
  12862.       pos: 16C6F Record type: 53C6
  12863.      
  12864.      LINK : error L2029: Unresolved externals:
  12865.      
  12866.      
  12867.  
  12868.      __RealCvtVector in file(s):
  12869.       M:SLIB.LIB(REALCVT)
  12870.      _abort in file(s):
  12871.       M:SLIB.LIB(CVTFAK)
  12872.      
  12873.      There were 3 errors detected
  12874.      
  12875.  
  12876. The error L2025 `symbol defined more than once' can in this case be
  12877. ignored.  However,   the unresolved externals `RealCvtVector'  and
  12878. `abort'  cannot  be ignored.  These two functions are referenced by the
  12879. function printf()  which has  been  included in the C function.  The
  12880. answer is to use as few  of  the compiler's  run time library functions
  12881. as possible,  use ROM  calls  instead with INT86() and INTDOSX() etc.
  12882.  
  12883.  
  12884. Adding High Resolution Graphics To Clipper With C
  12885.  
  12886. The most annoying omission from Clipper, in my opinion, is the lack of
  12887. high resolution graphics facilities. The following functions, written in
  12888. Turbo C, provide high resolution graphics to Clipper.
  12889.  
  12890. First we require a means to change the video display mode to a high
  12891. resolution graphics mode, and back to text mode. The IBM PC BIOS provides
  12892. the means for this and can be called from C as follows;
  12893.  
  12894.  
  12895.  
  12896.  
  12897.      /*         Servile Software Library For Clipper              */
  12898.      
  12899.      #include <nandef.h>
  12900.      #include <extend.h>
  12901.      #include <dos.h>
  12902.      
  12903.      CLIPPER s_smode()
  12904.      {
  12905.           /* Set Video Mode */
  12906.           /* From Clipper use s_smode(mode) */
  12907.      
  12908.           union REGS inreg,outreg;
  12909.      
  12910.           inreg.h.al = _parni(1);
  12911.           inreg.h.ah = 0x00;
  12912.           int86 (0x10, &inreg, &outreg);
  12913.      
  12914.      
  12915.      /*  1 40x25 colour text
  12916.           2 40x25 bw text
  12917.           3 80x25 colour text
  12918.           4 320x200 4 colour graphics
  12919.           5 320x200 4 colour graphics colour burst off
  12920.           6 640x200 2 colour graphics
  12921.           etc
  12922.      */
  12923.           _ret();
  12924.      }
  12925.  
  12926. Having set the computer into graphics mode, how about setting pixels to a
  12927. specified colour?
  12928.  
  12929.      
  12930.      /*         Servile Software Library For Clipper              */
  12931.      
  12932.      #include <nandef.h>
  12933.      #include <extend.h>
  12934.      #include <dos.h>
  12935.      
  12936.      CLIPPER s_plot()
  12937.      {
  12938.      
  12939.           union REGS inreg,outreg;
  12940.      
  12941.           /* Sets a pixel at the specified coordinates to the specified
  12942.      colour. */
  12943.      
  12944.           inreg.h.bh = 0x00;
  12945.           inreg.x.cx = _parni(1);
  12946.           inreg.x.dx = _parni(2);
  12947.           inreg.h.al = _parni(3);
  12948.           inreg.h.ah = 0x0C;
  12949.           int86(0x10, &inreg, &outreg);
  12950.      }
  12951.      
  12952. Line drawing and circles are handled by these two functions;
  12953.  
  12954.      
  12955.      /*         Servile Software Library For Clipper              */
  12956.      
  12957.      #include <nandef.h>
  12958.      #include <extend.h>
  12959.      #include <dos.h>
  12960.      
  12961.      CLIPPER s_line()
  12962.      {
  12963.      
  12964.           union REGS inreg,outreg;
  12965.      
  12966.           /* Draws a straight line from (a,b) to (c,d) in colour col */
  12967.      
  12968.           int a;
  12969.           int b;
  12970.           int c;
  12971.           int d;
  12972.           int col;
  12973.           int u;
  12974.           int v;
  12975.           int d1x;
  12976.           int d1y;
  12977.           int d2x;
  12978.           int d2y;
  12979.           int m;
  12980.           int n;
  12981.           int s;
  12982.           int i;
  12983.      
  12984.           a = _parni(1);
  12985.           b = _parni(2);
  12986.           c = _parni(3);
  12987.           d = _parni(4);
  12988.           col = _parni(5);
  12989.      
  12990.           u = c - a;
  12991.           v = d - b;
  12992.           if (u == 0)
  12993.           {
  12994.                d1x = 0;
  12995.                m = 0;
  12996.           }
  12997.           else
  12998.           {
  12999.                m = abs(u);
  13000.                if (u < 0)
  13001.                     d1x = -1;
  13002.                else
  13003.                     if (u > 0)
  13004.                          d1x = 1;
  13005.           }
  13006.           if ( v == 0)
  13007.           {
  13008.                d1y = 0;
  13009.                n = 0;
  13010.           }
  13011.           else
  13012.           {
  13013.                n = abs(v);
  13014.                if (v < 0)
  13015.                     d1y = -1;
  13016.                else
  13017.                     if (v > 0)
  13018.                          d1y = 1;
  13019.           }
  13020.           if (m > n)
  13021.           {
  13022.                d2x = d1x;
  13023.                d2y = 0;
  13024.           }
  13025.           else
  13026.           {
  13027.                d2x = 0;
  13028.                d2y = d1y;
  13029.                m = n;
  13030.                n = abs(u);
  13031.           }
  13032.           s = (m / 2);
  13033.      
  13034.           inreg.h.al = (unsigned char)col;
  13035.           inreg.h.bh = 0x00;
  13036.           inreg.h.ah = 0x0C;
  13037.           for (i = 0; i <= m; i++)
  13038.           {
  13039.                inreg.x.cx = (unsigned int)(a);
  13040.                inreg.x.dx = (unsigned int)(b);
  13041.                int86(0x10, &inreg, &outreg);
  13042.                s += n;
  13043.                if (s >= m)
  13044.                {
  13045.                     s -= m;
  13046.                     a += d1x;
  13047.                     b += d1y;
  13048.                }
  13049.                else
  13050.                {
  13051.                     a += d2x;
  13052.                     b += d2y;
  13053.                }
  13054.           }
  13055.      }
  13056.      
  13057.      
  13058.  
  13059. This circle drawing function uses in-line assembler to speed up the
  13060. drawing process. It can easily be replaced with inreg and outreg
  13061. parameters as in the other functions, or the other functions can be
  13062. changed to in-line assembler.  Both methods are shown to illustrate
  13063. different ways of achieving the same result.
  13064.  
  13065.  
  13066.      /*         Servile Software Library For Clipper              */
  13067.      
  13068.      #include <nandef.h>
  13069.      #include <extend.h>
  13070.      #include <dos.h>
  13071.      
  13072.      
  13073.      void plot(int x, int y, unsigned char colour)
  13074.      {
  13075.           asm mov al , colour;
  13076.           asm mov bh , 00;
  13077.           asm mov cx , x;
  13078.           asm mov dx , y;
  13079.           asm mov ah , 0Ch;
  13080.           asm int 10h;
  13081.      }
  13082.      
  13083.      int getmode()
  13084.      {
  13085.           /* Returns current video mode  and number of columns in ncols
  13086.      */
  13087.      
  13088.           asm mov ah , 0Fh;
  13089.           asm int 10h;
  13090.           return(_AL);
  13091.      }
  13092.      
  13093.      
  13094.      CLIPPER s_circle()
  13095.      {
  13096.           int x_centre;
  13097.           int y_centre;
  13098.           int radius;
  13099.           int colour;
  13100.           int x,y,delta;
  13101.           int startx,endx,x1,starty,endy,y1;
  13102.           int asp_ratio;
  13103.      
  13104.           x_centre = _parni(1);
  13105.           y_centre = _parni(2);
  13106.           radius = _parni(3);
  13107.           colour = _parni(4);
  13108.      
  13109.      
  13110.      
  13111.      
  13112.           if (getmode() == 6)
  13113.                asp_ratio = 22;
  13114.           else
  13115.                asp_ratio = 13;
  13116.      
  13117.           y = radius;
  13118.           delta = 3 - 2 * radius;
  13119.      
  13120.           for(x = 0; x < y; )
  13121.           {
  13122.                starty = y * asp_ratio / 10;
  13123.                endy = (y + 1) * asp_ratio / 10;
  13124.                startx = x * asp_ratio / 10;
  13125.                endx = (x + 1) * asp_ratio / 10;
  13126.      
  13127.                for(x1 = startx; x1 < endx; ++x1)
  13128.                {
  13129.                     plot(x1+x_centre,y+y_centre,colour);
  13130.                     plot(x1+x_centre,y_centre - y,colour);
  13131.                     plot(x_centre - x1,y_centre - y,colour);
  13132.                     plot(x_centre - x1,y + y_centre,colour);
  13133.                }
  13134.      
  13135.                for(y1 = starty; y1 < endy; ++y1)
  13136.                {
  13137.                     plot(y1+x_centre,x+y_centre,colour);
  13138.                     plot(y1+x_centre,y_centre - x,colour);
  13139.                     plot(x_centre - y1,y_centre - x,colour);
  13140.                     plot(x_centre - y1,x + y_centre,colour);
  13141.                }
  13142.      
  13143.                if (delta < 0)
  13144.                     delta += 4 * x + 6;
  13145.                else
  13146.                {
  13147.                     delta += 4*(x-y)+10;
  13148.                     y--;
  13149.                }
  13150.                x++;
  13151.           }
  13152.      
  13153.      
  13154.      
  13155.      
  13156.           if(y)
  13157.           {
  13158.                starty = y * asp_ratio / 10;
  13159.                endy = (y + 1) * asp_ratio / 10;
  13160.                startx = x * asp_ratio / 10;
  13161.                endx = (x + 1) * asp_ratio / 10;
  13162.                for(x1 = startx; x1 < endx; ++x1)
  13163.                {
  13164.                     plot(x1+x_centre,y+y_centre,colour);
  13165.                     plot(x1+x_centre,y_centre - y,colour);
  13166.                     plot(x_centre - x1,y_centre - y,colour);
  13167.                     plot(x_centre - x1,y + y_centre,colour);
  13168.                }
  13169.      
  13170.                for(y1 = starty; y1 < endy; ++y1)
  13171.                {
  13172.                     plot(y1+x_centre,x+y_centre,colour);
  13173.                     plot(y1+x_centre,y_centre - x,colour);
  13174.                     plot(x_centre - y1,y_centre - x,colour);
  13175.                     plot(x_centre - y1,x + y_centre,colour);
  13176.                }
  13177.           }
  13178.      }
  13179.      
  13180.  
  13181. The Clipper facilities for displaying text on the screen, @....SAY and ?
  13182. do not work when the monitor is in graphics mode. You then need the
  13183. following function to allow text to be displayed in a graphics mode;
  13184.  
  13185.      
  13186.      /*         Servile Software Library For Clipper              */
  13187.      
  13188.      #include <nandef.h>
  13189.      #include <extend.h>
  13190.      #include <dos.h>
  13191.      
  13192.      int sgetmode(int *ncols)
  13193.      {
  13194.           /* Returns current video mode  and number of columns in ncols
  13195.      */
  13196.      
  13197.           union REGS inreg,outreg;
  13198.      
  13199.           inreg.h.ah = 0x0F;
  13200.           int86(0x10, &inreg, &outreg);
  13201.           *ncols = outreg.h.ah;
  13202.           return(outreg.h.al);
  13203.      }
  13204.      
  13205.      void at(int row, int col)
  13206.      {
  13207.           asm mov bh , 0;
  13208.           asm mov dh , row;
  13209.           asm mov dl , col;
  13210.           asm mov ah , 02h;
  13211.           asm int 10h;
  13212.      }
  13213.      
  13214.      
  13215.      
  13216.      
  13217.      CLIPPER s_say()
  13218.      {
  13219.           char *output;
  13220.           int p = 0;
  13221.           unsigned char page;
  13222.           unsigned char text;
  13223.           int n;
  13224.           int r;
  13225.           int c;
  13226.           int attribute;
  13227.      
  13228.           output = _parc(1);
  13229.           r = _parni(2);
  13230.           c = _parni(3);
  13231.           attribute = _parni(4);
  13232.      
  13233.           asm mov ah , 0Fh;
  13234.           asm int 10h;
  13235.           asm mov page, bh;
  13236.      
  13237.           sgetmode(&n);
  13238.      
  13239.           at(r,c);
  13240.      
  13241.           while (output[p])
  13242.           {
  13243.                text = output[p++];
  13244.                asm mov bh , page;
  13245.                asm mov bl , attribute;
  13246.                asm mov cx , 01h;
  13247.                asm mov ah , 09h;
  13248.                asm mov al , text;
  13249.                asm int 10h;
  13250.                c++;
  13251.                if (c < (n-1))
  13252.                     at( r, c);
  13253.                else
  13254.                {
  13255.                     c = 0;
  13256.                     at(++r,0);
  13257.                }
  13258.           }
  13259.      }
  13260.      
  13261.  
  13262.  
  13263.  
  13264. When drawing graphs, it is often required to fill in areas of the graph
  13265. in different patterns. This is a graphics function to fill boundered
  13266. shapes with a specified hatching pattern providing a means to achieve
  13267. more usable graphs;
  13268.  
  13269.  
  13270.      
  13271.      /*         Servile Software Library For Clipper              */
  13272.      
  13273.      #include <nandef.h>
  13274.      #include <extend.h>
  13275.      #include <dos.h>
  13276.      
  13277.      int pixset(int x, int y)
  13278.      {
  13279.           /* Returns the colour of the specified pixel */
  13280.      
  13281.           asm mov cx ,x;
  13282.           asm mov dx ,y;
  13283.           asm mov ah ,0Dh;
  13284.           asm int 10h;
  13285.           return(_AL);
  13286.      }
  13287.      
  13288.      CLIPPER s_fill()
  13289.      {
  13290.           /* Fill a boundered shape using a hatch pattern */
  13291.      
  13292.           int mode;
  13293.           int xa;
  13294.           int ya;
  13295.           int bn;
  13296.           int byn;
  13297.           int x;
  13298.           int y;
  13299.           int col;
  13300.           int pattern;
  13301.           int maxx;
  13302.           int maxy;
  13303.           int hatch[10][8] = { 255,255,255,255,255,255,255,255,
  13304.                                    128,64,32,16,8,4,2,1,
  13305.                                    1,2,4,8,16,32,64,128,
  13306.                                    1,2,4,8,8,4,2,1,
  13307.                                    238,238,238,238,238,238,238,238,
  13308.                                    170,85,170,85,170,85,170,85,
  13309.                                    192,96,48,24,12,6,3,1,
  13310.                                    62,62,62,0,227,227,227,0,
  13311.                                    129,66,36,24,24,36,66,129,
  13312.                                    146,36,146,36,146,36,146,36};
  13313.      
  13314.           /* Patterns for fill, each integer describes a row of dots */
  13315.      
  13316.      
  13317.      
  13318.      
  13319.           x = _parni(1);
  13320.           y = _parni(2);
  13321.           col = _parni(3);
  13322.           pattern = _parni(4);
  13323.      
  13324.           mode = getmode();
  13325.      
  13326.           switch(mode)
  13327.           {
  13328.                case 0:
  13329.                case 1:
  13330.                case 2:
  13331.                case 3: break;
  13332.                case 4:
  13333.                case 9:
  13334.                case 13:
  13335.                case 19:
  13336.                case 5: maxx = 320;
  13337.                          maxy = 200;
  13338.                          break;
  13339.                case 14:
  13340.                case 10:
  13341.                case 6: maxx = 640;
  13342.                          maxy = 200;
  13343.                          break;
  13344.                case 7: maxx = 720;
  13345.                          maxy = 400;
  13346.                          break;
  13347.                case 8: maxx = 160;
  13348.                          maxy = 200;
  13349.                          break;
  13350.                case 15:
  13351.                case 16: maxx = 640;
  13352.                           maxy = 350;
  13353.                           break;
  13354.                case 17:
  13355.                case 18: maxx = 640;
  13356.                           maxy = 480;
  13357.                           break;
  13358.      
  13359.           }
  13360.      
  13361.           xa = x;
  13362.           ya = y;  /* Save Origin */
  13363.      
  13364.           if(pixset(x,y))
  13365.                return;
  13366.      
  13367.           bn = 1;
  13368.           byn = 0;
  13369.      
  13370.      
  13371.      
  13372.      
  13373.           do
  13374.           {
  13375.                if (hatch[pattern][byn] != 0)
  13376.                {  /* If blank ignore */
  13377.                     do
  13378.                     {
  13379.                          if ((bn & hatch[pattern][byn]) == bn)
  13380.                          {
  13381.                               asm mov al , col;
  13382.                               asm mov bh , 00;
  13383.                               asm mov cx , x;
  13384.                               asm mov dx , y;
  13385.                               asm mov ah , 0Ch;
  13386.                               asm int 10h;
  13387.                          }
  13388.                          x--;
  13389.                          bn <<= 1;
  13390.                          if (bn > 128)
  13391.                               bn = 1;
  13392.                     }
  13393.                     while(!pixset(x,y) && (x > -1));
  13394.      
  13395.                     x = xa + 1;
  13396.                     bn = 128;
  13397.      
  13398.                     do
  13399.                     {
  13400.                          if ((bn & hatch[pattern][byn]) == bn)
  13401.                          {
  13402.                               asm mov al , col;
  13403.                               asm mov bh , 00;
  13404.                               asm mov cx , x;
  13405.                               asm mov dx , y;
  13406.                               asm mov ah , 0Ch;
  13407.                               asm int 10h;
  13408.                          }
  13409.                          x++;
  13410.                          bn >>=1;
  13411.                          if (bn <1)
  13412.                               bn = 128;
  13413.                     }
  13414.                     while((!pixset(x,y)) && (x <= maxx));
  13415.                }
  13416.                x = xa;
  13417.                y--;
  13418.                bn = 1;
  13419.                byn++;
  13420.                if (byn > 7)
  13421.                     byn = 0;
  13422.      
  13423.      
  13424.      
  13425.      
  13426.           }
  13427.           while(!pixset(x,y) && ( y > -1));
  13428.      
  13429.           /* Now travel downwards */
  13430.      
  13431.           y = ya + 1;
  13432.      
  13433.           byn = 7;
  13434.           bn = 1;
  13435.           do
  13436.           {
  13437.                /* Travel left */
  13438.                if (hatch[pattern][byn] !=0)
  13439.                {
  13440.                     do
  13441.                     {
  13442.                          if ((bn & hatch[pattern][byn]) == bn)
  13443.                          {
  13444.                               asm mov al , col;
  13445.                               asm mov bh , 00;
  13446.                               asm mov cx , x;
  13447.                               asm mov dx , y;
  13448.                               asm mov ah , 0Ch;
  13449.                               asm int 10h;
  13450.                          }
  13451.                          x--;
  13452.                          bn <<= 1;
  13453.                          if (bn > 128)
  13454.                               bn = 1;
  13455.                     }
  13456.                     while(!pixset(x,y) && (x > -1));
  13457.      
  13458.                     /* Back to x origin */
  13459.                     x = xa + 1 ;
  13460.                     bn = 128;
  13461.      
  13462.                     /* Travel right */
  13463.                     do
  13464.                     {
  13465.                          if ((bn & hatch[pattern][byn]) == bn)
  13466.                          {
  13467.                               asm mov al , col;
  13468.                               asm mov bh , 00;
  13469.                               asm mov cx , x;
  13470.                               asm mov dx , y;
  13471.                               asm mov ah , 0Ch;
  13472.                               asm int 10h;
  13473.                          }
  13474.                          x++;
  13475.                          bn >>=1;
  13476.      
  13477.      
  13478.      
  13479.      
  13480.                          if (bn <1)
  13481.                               bn = 128;
  13482.                     }
  13483.                     while((!pixset(x,y)) && (x <= maxx));
  13484.                }
  13485.                x = xa;
  13486.                bn = 1;
  13487.                y++;
  13488.                byn--;
  13489.                if (byn < 0)
  13490.                     byn = 7;
  13491.           }
  13492.           while((!pixset(x,y)) && (y <= maxy));
  13493.      }
  13494.      
  13495.      
  13496.      
  13497.                                     
  13498.                        SPELL - AN EXAMPLE PROGRAM
  13499.  
  13500. It has been said that example programs provide a good way of learning a
  13501. new computer language. On that basis the following simple program is
  13502. offered as an example of making use of the dynamic memory allocation
  13503. provided by DOS.
  13504.  
  13505. This is a spell checker for ASCII (text) documents, written in, and
  13506. making use of Borland's Turbo C text graphics facilities for displaying
  13507. windows of text.
  13508.  
  13509.  
  13510.      /* Spell checker for ascii documents */
  13511.      /* Compile with -mc (compact memory model) and unsigned characters
  13512.      */
  13513.      
  13514.      
  13515.      #include <stdio.h>
  13516.      #include <conio.h>
  13517.      #include <fcntl.h>
  13518.      #include <io.h>
  13519.      #include <dos.h>
  13520.      #include <string.h>
  13521.      #include <alloc.h>
  13522.      #include <ctype.h>
  13523.      #include <stdlib.h>
  13524.      #include <bios.h>
  13525.      #include <dir.h>
  13526.      #include <stat.h>
  13527.      
  13528.      #define  ON           0x06
  13529.      #define  OFF         0x20
  13530.      #define  MAXIMUM     15000
  13531.      #define  WORDLEN     20
  13532.      #define  LEFTMARGIN  1
  13533.      
  13534.      union REGS inreg,outreg;
  13535.      
  13536.      char *dicname;
  13537.      char *dic[MAXIMUM];      /* Array of text lines */
  13538.      char word[31];
  13539.      char comp[31];
  13540.      char fname[160];
  13541.      int lastelem;
  13542.      char changed;
  13543.      char *ignore[100];
  13544.      int lastign;
  13545.      int insert;
  13546.      int n;
  13547.      int bp;
  13548.      int mp;
  13549.      int tp;
  13550.      int result;
  13551.      
  13552.      
  13553.      
  13554.      
  13555.      char *text;
  13556.      char *textsav;
  13557.      
  13558.      void AT(int, int);
  13559.      void BANNER(void);
  13560.      int COMPARE(void);
  13561.      void CORRECT(void);
  13562.      void FATAL(char *);
  13563.      void FILERR(char *);
  13564.      void GETDIC(void);
  13565.      void IGNORE(void);
  13566.      void INSERT(void);
  13567.      int MATCHSTR(char *, char *);
  13568.      void SPELL(void);
  13569.      void UPDATE(void);
  13570.      
  13571.      void CURSOR(char status)
  13572.      {
  13573.           /* Toggle cursor display on and off */
  13574.      
  13575.           union REGS inreg,outreg;
  13576.      
  13577.           inreg.h.ah = 1;
  13578.           inreg.h.ch = (unsigned char)status;
  13579.           inreg.h.cl = 7;
  13580.           int86(0x10,&inreg,&outreg);
  13581.      }
  13582.      
  13583.      void DISPLAY(char *text)
  13584.      {
  13585.           /* Display 'text' expanding tabs and newline characters */
  13586.      
  13587.           while(*text)
  13588.           {
  13589.                switch(*text)
  13590.                {
  13591.                     case '\n':  cputs("\r\n");
  13592.                                    break;
  13593.                     case '\t':  cputs("      ");
  13594.                                    break;
  13595.                     default:  putch(*text);
  13596.                }
  13597.                text++;
  13598.           }
  13599.      }
  13600.      
  13601.      
  13602.      
  13603.      
  13604.      void GETDIC()
  13605.      {
  13606.           /* Read dictionary into memory */
  13607.      
  13608.           FILE *fp;
  13609.           char *p;
  13610.           int poscr;
  13611.           int handle;
  13612.      
  13613.           window(1,22,80,24);
  13614.           clrscr();
  13615.           gotoxy(28,2);
  13616.           cprintf("Reading Dictionary....");
  13617.      
  13618.           changed = 0;
  13619.           lastelem = 0;
  13620.      
  13621.           dicname = searchpath("spell.dic");
  13622.           handle = open(dicname,O_RDWR);
  13623.           if (handle < 0)
  13624.                FILERR("spell.dic");
  13625.      
  13626.           fp = fdopen(handle,"r");
  13627.           if (fp == NULL)
  13628.                FILERR("spell.dic");
  13629.      
  13630.           do
  13631.           {
  13632.                dic[lastelem] = calloc(WORDLEN,1);
  13633.                if (dic[lastelem])
  13634.                {
  13635.                     p = fgets(dic[lastelem],79,fp);
  13636.                     /* Remove carriage return from end of text line */
  13637.                     poscr = (int)strlen(dic[lastelem]) - 1;
  13638.                     if (dic[lastelem][poscr] == '\n')
  13639.                          dic[lastelem][poscr] = 0;
  13640.                }
  13641.                else
  13642.                     FATAL("Unable To Allocate Memory");
  13643.           }
  13644.           while((p != NULL) && (lastelem++ < MAXIMUM));
  13645.      
  13646.           lastelem--;
  13647.      
  13648.           fclose(fp);
  13649.      }
  13650.      
  13651.      
  13652.      
  13653.      
  13654.      void UPDATE()
  13655.      {
  13656.           FILE *fp;
  13657.           int n;
  13658.      
  13659.           if (changed)
  13660.           {
  13661.                window(1,22,80,24);
  13662.                clrscr();
  13663.                gotoxy(27,2);
  13664.                cprintf("Updating Dictionary....");
  13665.      
  13666.                fp = fopen(dicname,"w+");
  13667.                if (fp == NULL)
  13668.                     FILERR("spell.dic");
  13669.      
  13670.                for(n = 0; n <= lastelem; n++)
  13671.                     fprintf(fp,"%s\n",dic[n]);
  13672.      
  13673.                fclose(fp);
  13674.           }
  13675.      }
  13676.      
  13677.      void IGNORE()
  13678.      {
  13679.           /* Add a word to the ignore table */
  13680.      
  13681.           if (lastign < 100)
  13682.           {
  13683.                ignore[lastign] = calloc(strlen(word) + 1,1);
  13684.                if (ignore[lastign])
  13685.                     strcpy(ignore[lastign++],comp);
  13686.                else
  13687.                {
  13688.                     clrscr();
  13689.                     cprintf("No available memory for new words!\r\nPress
  13690.      A key....");
  13691.                     bioskey(0);
  13692.                }
  13693.            }
  13694.            else
  13695.            {
  13696.                clrscr();
  13697.                cprintf("No available memory for new words!\r\nPress A
  13698.      key....");
  13699.                bioskey(0);
  13700.            }
  13701.      }
  13702.      
  13703.      
  13704.      
  13705.      
  13706.      void FATAL(char *text)
  13707.      {
  13708.           /* Fatal error drop out */
  13709.      
  13710.           textcolor(LIGHTGRAY);
  13711.           textbackground(BLACK);
  13712.           window(1,1,80,25);
  13713.           clrscr();
  13714.           printf("SERVILE SOFTWARE\n\nSPELL V1.7\nFATAL ERROR:
  13715.      %s\n\n",text);
  13716.           CURSOR(ON);
  13717.           exit(0);
  13718.      }
  13719.      
  13720.      void FILERR(char *fname)
  13721.      {
  13722.           char text[60];
  13723.      
  13724.           strcpy(text,"Unable To Access: ");
  13725.           strcat(text,fname);
  13726.           FATAL(text);
  13727.      }
  13728.      
  13729.      int COMPARE()
  13730.      {
  13731.           char **p;
  13732.      
  13733.           /* Check Ignore table */
  13734.           for(p = ignore; p <= &ignore[lastign]; p++)
  13735.                if (strcmp(comp,*p) == 0)
  13736.                     return(1);
  13737.      
  13738.           /* Binary search of dictionary file */
  13739.           bp = 0;
  13740.           tp = lastelem;
  13741.           mp = (tp + bp) / 2;
  13742.      
  13743.      
  13744.      
  13745.      
  13746.           while((result = strcmp(dic[mp],comp)) != 0)
  13747.           {
  13748.                if (mp >= tp)
  13749.                {
  13750.                     /* Not found! */
  13751.                     insert = mp;
  13752.                     if (result > 0)
  13753.                          insert--;
  13754.                     return(0);
  13755.                }
  13756.                if (result < 0)
  13757.                     bp = mp + 1;
  13758.                else
  13759.                     tp = mp - 1;
  13760.      
  13761.                mp = (bp + tp) / 2;
  13762.           }
  13763.           return(1);
  13764.      }
  13765.      
  13766.      void INSERT()
  13767.      {
  13768.           int n;
  13769.      
  13770.           changed = 1;
  13771.           lastelem++;
  13772.           n = lastelem;
  13773.      
  13774.           dic[n] = calloc(WORDLEN,1);
  13775.      
  13776.           if (dic[n] == NULL)
  13777.           {
  13778.                clrscr();
  13779.                cprintf("No available memory for new words!\r\nPress A
  13780.      key....");
  13781.                bioskey(0);
  13782.                free(dic[n]);
  13783.                lastelem--;
  13784.                return;
  13785.           }
  13786.      
  13787.           while(n > (insert + 1))
  13788.           {
  13789.                strcpy(dic[n],dic[n-1]);
  13790.                n--;
  13791.           };
  13792.      
  13793.           strcpy(dic[insert + 1],comp);
  13794.      }
  13795.      
  13796.      
  13797.      
  13798.      
  13799.      void SPELL()
  13800.      {
  13801.           FILE *target;
  13802.           FILE *source;
  13803.           char *p;
  13804.           char *x;
  13805.           char temp[256];
  13806.           char dat1[1250];
  13807.           char dat2[1250];
  13808.           int c;
  13809.           int m;
  13810.           int found;
  13811.           int curpos;
  13812.           int key;
  13813.           int row;
  13814.           int col;
  13815.           int srow;
  13816.           int scol;
  13817.      
  13818.           window(1,1,80,20);
  13819.           textcolor(BLACK);
  13820.           textbackground(WHITE);
  13821.      
  13822.           /* Open temporary file to take spell checked copy */
  13823.           target = fopen("spell.$$$","w+");
  13824.      
  13825.           source = fopen(fname,"r");
  13826.      
  13827.           if (source == NULL)
  13828.                FILERR(fname);
  13829.      
  13830.           lastign = 0;
  13831.      
  13832.           do
  13833.           {
  13834.                clrscr();
  13835.      
  13836.                text = dat1;
  13837.      
  13838.                p = text;
  13839.      
  13840.                textsav = dat2;
  13841.      
  13842.                strcpy(text,"");
  13843.      
  13844.                /* Display read text */
  13845.                row = wherey();
  13846.                col = wherex();
  13847.      
  13848.      
  13849.      
  13850.      
  13851.                for(m = 0; m < 15; m++)
  13852.                {
  13853.                     x = fgets(temp,200,source);
  13854.                     if (x)
  13855.                     {
  13856.                          strcat(text,temp);
  13857.                          DISPLAY(temp);
  13858.                     }
  13859.                     if (wherey() > 18)
  13860.                          break;
  13861.                }
  13862.      
  13863.                /* return cursor to start position */
  13864.                gotoxy(col,row);
  13865.      
  13866.                do
  13867.                {
  13868.                     memset(word,32,30);
  13869.                     curpos = 0;
  13870.                     do
  13871.                     {
  13872.                          c = *text++;
  13873.                          if ((isalpha(c)) || (c == '-') && (curpos != 0))
  13874.                               word[curpos++] = c;
  13875.                     }
  13876.                     while(((isalpha(c)) || (c == '-') && (curpos != 0))
  13877.                             && (curpos < 30));
  13878.                     word[curpos] = 0;
  13879.                     strcpy(comp,word);
  13880.                     strupr(comp);
  13881.      
  13882.                     if (*comp != 0)
  13883.                     {
  13884.                          found = COMPARE();
  13885.                          if (!found){
  13886.                               textbackground(RED);
  13887.                               textcolor(WHITE);
  13888.                          }
  13889.                     }
  13890.                     else
  13891.                          found = 1;
  13892.      
  13893.                     srow = wherey();
  13894.                     scol = wherex();
  13895.      
  13896.                     cputs(word);
  13897.                     textbackground(WHITE);
  13898.                     textcolor(BLACK);
  13899.      
  13900.      
  13901.      
  13902.      
  13903.      
  13904.                     switch(c)
  13905.                     {
  13906.                          case '\n': cputs("\r\n");
  13907.                                       break;
  13908.                          case '\t': cputs("       ");
  13909.                                       break;
  13910.                          default: putch(c);
  13911.                     }
  13912.      
  13913.                     row = wherey();
  13914.                     col = wherex();
  13915.      
  13916.                     if (!found)
  13917.                     {
  13918.                          window(1,22,80,24);
  13919.                          clrscr();
  13920.                          cputs("Unknown word ");
  13921.                          textcolor(BLUE);
  13922.                          cprintf("%s ",word);
  13923.                          textcolor(BLACK);
  13924.                          cputs("[A]dd  [I]gnore  [C]orrect  [S]kip");
  13925.                          do
  13926.                          {
  13927.                               key = toupper(getch());
  13928.                               if (key == 27)
  13929.                                    key = 'Q';
  13930.                          }
  13931.                          while(strchr("AICSQ",key) == NULL);
  13932.      
  13933.                          switch(key)
  13934.                          {
  13935.                               case 'A':INSERT();
  13936.                                          break;
  13937.      
  13938.                               case 'C':CORRECT();
  13939.                                          break;
  13940.      
  13941.                               case 'I':IGNORE();
  13942.                                          break;
  13943.                          }
  13944.      
  13945.      
  13946.      
  13947.      
  13948.                          if (key == 'C')
  13949.                          {
  13950.                               clrscr();
  13951.                               gotoxy(1,1);
  13952.                               strcpy(textsav,--text);
  13953.                               /* Delete old word */
  13954.                               text -= strlen(comp);
  13955.                               *text = 0;
  13956.                               /* Insert new word */
  13957.                               strcat(text,word);
  13958.                               /* Append remainder of text */
  13959.                               strcat(text,textsav);
  13960.                               text += strlen(word);
  13961.                               text++;
  13962.                               /* Length of text may have changed ! */
  13963.                               if (strlen(word) < strlen(comp))
  13964.                                    col -= (strlen(comp) - strlen(word));
  13965.                               window(1,1,80,20);
  13966.                               clrscr();
  13967.                               DISPLAY(p);
  13968.                          }
  13969.                          else
  13970.                          {
  13971.                               clrscr();
  13972.                               gotoxy(29,2);
  13973.                               cputs("Checking Spelling....");
  13974.                               window(1,1,80,20);
  13975.                               gotoxy(scol,srow);
  13976.                               cputs(word);
  13977.                          }
  13978.                          window(1,1,80,20);
  13979.                          gotoxy(col,row);
  13980.                     }
  13981.                }
  13982.                while((*text) && (key != 'Q'));
  13983.                fprintf(target,"%s",p);
  13984.           }
  13985.           while((x != NULL) && (key != 'Q'));
  13986.      
  13987.           window(1,22,80,24);
  13988.           clrscr();
  13989.           gotoxy(27,2);
  13990.           cprintf("Writing Updated File....");
  13991.      
  13992.      
  13993.      
  13994.      
  13995.           do
  13996.           {
  13997.                p = fgets(temp,200,source);
  13998.                if (p)
  13999.                     fprintf(target,"%s",temp);
  14000.           }
  14001.           while(p);
  14002.      
  14003.           fclose(target);
  14004.           fclose(source);
  14005.      
  14006.           /* Now transfer spell.$$$ to fname */
  14007.           unlink(fname);
  14008.           rename("SPELL.$$$",fname);
  14009.      }
  14010.      
  14011.      void CORRECT()
  14012.      {
  14013.           /* Locate a good match and return word */
  14014.      
  14015.           char text[51];
  14016.           int m;
  14017.           int n;
  14018.           int key;
  14019.      
  14020.           window(1,22,80,24);
  14021.           clrscr();
  14022.           gotoxy(25,2);
  14023.           cprintf("Searching For Alternatives....");
  14024.      
  14025.           /* Remove any pending key strokes from keyboard buffer */
  14026.           while(kbhit())
  14027.                getch();
  14028.      
  14029.           for(n = 0; n <= lastelem; n++)
  14030.           {
  14031.                if (MATCHSTR(dic[n],comp))
  14032.                {
  14033.                     strcpy(text,dic[n]);
  14034.                     if (strlen(word) <= strlen(text))
  14035.                     {
  14036.                          for (m = 0; m < strlen(word); m++)
  14037.                          {
  14038.                               if (isupper(word[m]))
  14039.                                    text[m] = toupper(text[m]);
  14040.                               else
  14041.                                    text[m] = tolower(text[m]);
  14042.                          }
  14043.      
  14044.      
  14045.      
  14046.      
  14047.                          for(m = strlen(word); m < strlen(text); m++)
  14048.                               if (isupper(word[strlen(word)]))
  14049.                                    text[m] = toupper(text[m]);
  14050.                               else
  14051.                                    text[m] = tolower(text[m]);
  14052.                     }
  14053.                     else
  14054.                     {
  14055.                          for (m = 0; m < strlen(text); m++)
  14056.                          {
  14057.                               if (isupper(word[m]))
  14058.                                    text[m] = toupper(text[m]);
  14059.                               else
  14060.                                    text[m] = tolower(text[m]);
  14061.                          }
  14062.                     }
  14063.                     clrscr();
  14064.                     cprintf("Replace ");
  14065.                     textcolor(BLUE);
  14066.                     cprintf("%s ",word);
  14067.                     textcolor(BLACK);
  14068.                     cprintf("With ");
  14069.                     textcolor(BLUE);
  14070.                     cprintf("%s",text);
  14071.                     textcolor(BLACK);
  14072.                     cprintf(" Yes No Continue");
  14073.                     do
  14074.                     {
  14075.                          key = toupper(getch());
  14076.                     }
  14077.                     while(strchr("YNC",key) == NULL);
  14078.                     if (key == 'Y')
  14079.                     {
  14080.                          strcpy(word,text);
  14081.                          return;
  14082.                     }
  14083.                     clrscr();
  14084.                     gotoxy(25,2);
  14085.                     cprintf("Searching For Alternatives....");
  14086.      
  14087.                     /* Remove any pending key strokes from keyboard
  14088.      buffer */
  14089.                     while(kbhit())
  14090.                          getch();
  14091.      
  14092.                     if (key == 'C')
  14093.                          return;
  14094.                }
  14095.           }
  14096.           clrscr();
  14097.           gotoxy(23,2);
  14098.      
  14099.      
  14100.      
  14101.      
  14102.           cprintf("NO ALTERNATIVES FOUND! (Press a key)");
  14103.           bioskey(0);
  14104.           return;
  14105.      }
  14106.      
  14107.      
  14108.      int MATCHSTR(char *src, char *tgt)
  14109.      {
  14110.           /* Compare two words and return non zero if they are similar */
  14111.      
  14112.           int match;
  14113.           int result;
  14114.           int strsrc;
  14115.           int strtgt;
  14116.           int longest;
  14117.      
  14118.           strtgt = strlen(strupr(tgt));
  14119.           strsrc = strlen(strupr(src));
  14120.      
  14121.           longest = max(strtgt,strsrc);
  14122.      
  14123.           match = 0;
  14124.      
  14125.           if(strtgt > strsrc)
  14126.           {
  14127.                for(; *src ; match += (*src++ == *tgt++))
  14128.                     ;
  14129.           }
  14130.           else
  14131.           {
  14132.                for(; *tgt ; match += (*src++ == *tgt++))
  14133.                     ;
  14134.           }
  14135.      
  14136.           result = (match * 100 / longest);
  14137.      
  14138.           /* result holds percentage similarity */
  14139.      
  14140.           if (result > 50)
  14141.                return(1);
  14142.           return(0);
  14143.      }
  14144.      
  14145.      void AT(int row, int col)
  14146.      {
  14147.           /* Position the text cursor */
  14148.           inreg.h.bh = 0;
  14149.           inreg.h.dh = row;
  14150.           inreg.h.dl = col;
  14151.           inreg.h.ah = 0x02;
  14152.           int86 (0x10, &inreg, &outreg);
  14153.      }
  14154.      
  14155.      void WRTCHA (unsigned char ch, unsigned char attrib,  int num)
  14156.      {
  14157.           /* Display a character num times in colour attrib */
  14158.           /* via the BIOS */
  14159.      
  14160.           inreg.h.al = ch;
  14161.           inreg.h.bh = 0;
  14162.           inreg.h.bl = attrib;
  14163.           inreg.x.cx = num;
  14164.           inreg.h.ah = 0x09;
  14165.           int86 (0x10, &inreg, &outreg);
  14166.      }
  14167.      
  14168.      void SHADE_BLOCK(int left,int top,int right,int bottom)
  14169.      {
  14170.           int c;
  14171.      
  14172.           AT(bottom,right);
  14173.           WRTCHA(223,56,1);
  14174.           AT(top,right);
  14175.           WRTCHA('ß',7,1);
  14176.           for (c = top+1; c < bottom; c++)
  14177.           {
  14178.                AT(c,right);
  14179.                WRTCHA(' ',7,1);
  14180.           }
  14181.           AT(bottom,left+1);
  14182.           WRTCHA('Ü',7,right-left);
  14183.      }
  14184.      
  14185.      
  14186.      
  14187.      
  14188.      void BOX(int l, int t, int r, int b)
  14189.      {
  14190.           /* Draws a single line box around a described area */
  14191.      
  14192.           int n;
  14193.           char top[81];
  14194.           char bottom[81];
  14195.           char tolc[5];
  14196.           char torc[5];
  14197.           char bolc[5];
  14198.           char borc[5];
  14199.           char hoor[5];
  14200.      
  14201.           sprintf(tolc,"%c",218);
  14202.           sprintf(bolc,"%c",192);
  14203.           sprintf(hoor,"%c",196);
  14204.           sprintf(torc,"%c",191);
  14205.           sprintf(borc,"%c",217);
  14206.      
  14207.                strcpy(top,tolc);
  14208.           strcpy(bottom,bolc);
  14209.           for(n = l + 1; n < r; n++)
  14210.           {
  14211.                strcat(top,hoor);
  14212.                strcat(bottom,hoor);
  14213.           }
  14214.           strcat(top,torc);
  14215.           strcat(bottom,borc);
  14216.      
  14217.           window(1,1,80,25);
  14218.                gotoxy(l,t);
  14219.           cputs(top);
  14220.           for (n = t + 1; n < b; n++)
  14221.           {
  14222.                gotoxy(l,n);
  14223.                putch(179);
  14224.                gotoxy(r,n);
  14225.                putch(179);
  14226.           }
  14227.           gotoxy(l,b);
  14228.           cputs(bottom);
  14229.      }
  14230.      
  14231.      
  14232.      
  14233.      
  14234.      void BANNER()
  14235.      {
  14236.           window (2,2,78,4);
  14237.           textcolor(BLACK);
  14238.           textbackground(GREEN);
  14239.           clrscr();
  14240.           SHADE_BLOCK(1,1,78,4);
  14241.           BOX(2,2,78,4);
  14242.           gotoxy(4,3);
  14243.           cprintf("Servile Software                SPELL CHECKER V1.7
  14244.                      (c)1992");
  14245.      }
  14246.      
  14247.      
  14248.      void main(int argc, char *argv[])
  14249.      {
  14250.           char *p;
  14251.           char tmp_name[160];
  14252.           char tmp_fname[160];
  14253.      
  14254.           if (argc != 2)
  14255.           {
  14256.                puts("\nERROR: Usage is SPELL document");
  14257.                exit(1);
  14258.           }
  14259.           else
  14260.                strcpy(fname,argv[1]);
  14261.      
  14262.           CURSOR(OFF);
  14263.      
  14264.           GETDIC();
  14265.      
  14266.           window(1,22,80,24);
  14267.           clrscr();
  14268.           gotoxy(28,2);
  14269.           cprintf("Making Backup File....");
  14270.      
  14271.           strcpy(tmp_fname,argv[1]);
  14272.      
  14273.           /* Remove extension from tmp_fname */
  14274.           p = strchr(tmp_fname,'.');
  14275.           if(p)
  14276.                *p = 0;
  14277.      
  14278.           /* Create backup file name using DOS */
  14279.           sprintf(tmp_name,"copy %s %s.!s! > NUL",argv[1],tmp_fname);
  14280.      
  14281.           system(tmp_name);
  14282.      
  14283.           window(1,1,80,25);
  14284.      
  14285.      
  14286.      
  14287.      
  14288.           textcolor(WHITE);
  14289.           textbackground(BLACK);
  14290.           clrscr();
  14291.           gotoxy(29,2);
  14292.           cprintf("Checking Spelling....");
  14293.      
  14294.           SPELL();
  14295.      
  14296.           UPDATE();
  14297.           window(1,1,80,25);
  14298.           textcolor(LIGHTGRAY);
  14299.           textbackground(BLACK);
  14300.           clrscr();
  14301.           CURSOR(ON);
  14302.      }
  14303.      
  14304.                                     
  14305.                          APPENDIX A - USING LINK
  14306.  
  14307.  
  14308.  
  14309. General Syntax:
  14310.  
  14311.      LINK [options] obj[,[exe][,[map][,[lib]]]][;]
  14312.  
  14313. `obj' is a list of object files to be linked. Each obj file name must be
  14314. separated by a + or a space. If you do not specify an extension, LINK
  14315. will assume .OBJ. `exe' allows you to specify a name for the executable
  14316. file. If this file name is ommited, LINK will use the first obj file name
  14317. and suffix it with .EXE. `map' is an optional map file name. If you
  14318. specify the name `NUL', no map file is produced. `lib' is a list of
  14319. library files to link. LINK searches each library file and only links in
  14320. modules which are referenced.
  14321.  
  14322.  
  14323. eg:
  14324.  
  14325.      LINK filea+fileb,myfile,NUL;
  14326.  
  14327. Links .obj files `filea.obj' and `fileb.obj' into .exe file `myfile.exe'
  14328. with no map file produced. The ; at the end of the line tells LINK that
  14329. there are no more parameters.
  14330.  
  14331.  
  14332.  
  14333. Using Overlays
  14334.  
  14335. Overlay .obj modules are specified by encasing the .obj name in
  14336. parenthesis in the link line.
  14337.  
  14338. eg:
  14339.  
  14340.      LINK filea + (fileb) + (filec),myfile,NUL;
  14341.  
  14342. Will link filea.obj fileb.obj and filec.obj with modules fileb.obj and
  14343. filec.obj as overlay code.
  14344.  
  14345.  
  14346. Overlay modules must use FAR call/return instructions.
  14347.  
  14348.  
  14349. Linker Options
  14350.  
  14351. All LINK options commence with a forward slash `/'. Options which accept
  14352. a number can accept a decimal number or a hex number prefixed 0X. eg:
  14353. 0X10 is interpreted as 10h, decimal 16.
  14354.  
  14355.  
  14356.  
  14357. Pause during Linking (/PAU)
  14358.  
  14359.      Tells LINK to wait before writing the .exe file to disk. LINK
  14360. displays a
  14361.      message and waits for you to press enter.
  14362.  
  14363. Display Linker Process Information (/I)
  14364.  
  14365.      Tells LINK to display information about the link process.
  14366.  
  14367. Pack Executable File (/E)
  14368.  
  14369.      Tells LINK to remove sequences of repeated bytes and to optimise the
  14370. load-time
  14371.      relocation table before creating the executable file. Symbolic debug
  14372.      information is stripped out of the file.
  14373.  
  14374. List Public Symbols (/M)
  14375.  
  14376.      Tells LINK to create a list of all public symbols defined in the
  14377.      object files
  14378.      in the MAP file.
  14379.  
  14380. Include Line Numbers In Map File (/LI)
  14381.  
  14382.      Tells LINK to include line numbers and associated addresses of the
  14383. source
  14384.      program in the MAP file.
  14385.  
  14386. Preserve Case Sensitivity (/NOI)
  14387.  
  14388.      By default LINK treats uppercase and lowercase letters as the same.
  14389. This
  14390.      option tells LINK that they are different.
  14391.  
  14392. Ignore Default Libraries (/NOD)
  14393.  
  14394.      Tells LINK not to search any library specified in the object files
  14395. to resolve
  14396.      external references.
  14397.  
  14398. Controlling Stack Size (/ST:n)
  14399.  
  14400.      Specifies the size of the stack segment where 'n' is the number of
  14401. bytes.
  14402.  
  14403. Setting Maximum Allocation Space (/CP:n)
  14404.  
  14405.      Tells LINK to write the parameter 'n' into the exe file header. When
  14406. the exe
  14407.      file is executed by DOS, 'n' 16 byte paragraphs of memory are
  14408. reserved. If 'n'
  14409.      is less than the minimum required, it will be set to the minimum.
  14410. This option
  14411.      is ESSENTIAL to free memory from the program. C programs free memory
  14412.      automatically on start-up, assembly language programs which want to
  14413. use
  14414.      dynamic memory allocation must be linked with this option set to a
  14415. minimum.
  14416.  
  14417. Setting Maximum Number Of Segments (/SE:n)
  14418.  
  14419.      Tells LINK how many segments a program is allowed to have. The
  14420. default is 128
  14421.      but 'n' can be any number between 1 and 3072.
  14422.  
  14423.  
  14424. Setting Overlay Interrupt (/O:n)
  14425.  
  14426.      Tells LINK which interrupt number will be used for passing control
  14427. to
  14428.      overlays. The default is 63. Valid values for 'n' are 0 through 255.
  14429.  
  14430. Ordering Segments (/DO)
  14431.  
  14432.      Tells LINK to use DOS segment ordering. This option is also enabled
  14433. by the
  14434.      MASM directive .DOSSEG.
  14435.  
  14436. Controlling Data Loading (/DS)
  14437.  
  14438.      By default LINK loads all data starting at the low end of the data
  14439. segment. At
  14440.      run time the DS register is set to the lowest possible address to
  14441. allow the
  14442.      entire data segment to be used. This option tells LINK to load all
  14443. data
  14444.      starting at the high end of the data segment.
  14445.  
  14446. Control Exe File Loading (/HI)
  14447.  
  14448.      Tells LINK to place the exe file as high as possible in memory.
  14449.  
  14450. Prepare for Debugging (/CO)
  14451.  
  14452.      Tells LINK to include symbolic debug information for use by
  14453. codeview.
  14454.  
  14455. Optimising Far Calls (/F)
  14456.  
  14457.      Tells LINK to translate FAR calls to NEAR calls where possible. This
  14458. results
  14459.      in faster code.
  14460.  
  14461. Disabling Far Call Optimisation (/NOF)
  14462.  
  14463.      Tells LINK not to translate FAR calls. This option is specified by
  14464. default.
  14465.  
  14466. Packing Contiguous Segments (/PAC:n)
  14467.  
  14468.      Tells LINK to group together neighbouring code segments, providing
  14469. more
  14470.      oportunities for FAR call translation. 'n' specifies the maximum
  14471. size of a
  14472.      segment. By default 'n' is 65530. This option is only relevant to
  14473. obj files
  14474.      created using FAR calls.
  14475.  
  14476. Disabling Segment Packing (/NOP)
  14477.  
  14478.      Disables segment packing. This option is specified by default.
  14479.  
  14480.  
  14481.  
  14482. Using Response Files
  14483.  
  14484. Linker options and file names may be specified in a response file. Each
  14485. file list starting on a new line instead of being separated by a comma.
  14486.  
  14487. eg:
  14488.  
  14489.      filea.obj fileb.obj
  14490.      myfile.exe
  14491.      NUL
  14492.      liba.lib libb.lib
  14493.      
  14494. A response file is specified to LINK by prefixing the response file name
  14495. with '@'.
  14496. eg:
  14497.  
  14498.      LINK @response
  14499.  
  14500.